ReorderableListView and Riverpod - flickering in release mode #642
Replies: 2 comments 6 replies
-
So what I'm doing now is: class _BlaList extends ConsumerStatefulWidget {
const _BlaList();
@override
_BlaListState createState() => _BlaListState();
}
class _BlaListState extends ConsumerState {
late List<Bla> blas;
@override
Widget build(BuildContext context) {
blas = ref.watch(blasProvider);
return ReorderableListView.builder(
itemCount: blas.length,
onReorder: (oldIndex, newIndex) {
final newBlas = ref
.read(blasProvider.notifier)
.moveBla(oldIndex: oldIndex, newIndex: newIndex);
setState(() {
blas = newBlas;
});
},
itemBuilder: (context, index) {
final bla = blas[index];
// ...
},
);
}
}
And This does the trick, but I find it awful and complex, and I am not even sure if it is correct. This also means that rebuilding the widget resulting from |
Beta Was this translation helpful? Give feedback.
-
The core issue is that Riverpod updates asynchronously. I found the simplest solution is to wrap import 'package:flutter/material.dart';
typedef ListItemWidgetBuilder<T> = Widget Function(
BuildContext context, T item);
class CachedReorderableListView<T> extends StatefulWidget {
const CachedReorderableListView(
{required this.list,
required this.itemBuilder,
required this.onReorder,
super.key});
final List<T> list;
final ListItemWidgetBuilder<T> itemBuilder;
final ReorderCallback onReorder;
@override
CachedReorderableListViewState<T> createState() =>
CachedReorderableListViewState<T>();
}
class CachedReorderableListViewState<T>
extends State<CachedReorderableListView<T>> {
late List<T> list;
@override
void initState() {
list = [...widget.list];
super.initState();
}
@override
void didUpdateWidget(covariant CachedReorderableListView<T> oldWidget) {
if (widget.list != oldWidget.list) {
list = [...widget.list];
}
super.didUpdateWidget(oldWidget);
}
void updateList(List<T> value) {
setState(() {
list = value;
});
}
@override
Widget build(BuildContext context) {
return ReorderableListView.builder(
itemCount: list.length,
itemBuilder: (context, index) => widget.itemBuilder(context, list[index]),
onReorder: (int oldIndex, int newIndex) {
setState(() {
if (oldIndex < newIndex) {
newIndex -= 1;
}
final item = list.removeAt(oldIndex);
list.insert(newIndex, item);
});
widget.onReorder(oldIndex, newIndex);
},
);
}
}
// Then in your widget
CachedReorderableListView(
onReorder: (int oldIndex, int newIndex) async {
// Trigger your Riverpod update
},
list: yourList,
itemBuilder: (context, item) {
// render a list item
},
); |
Beta Was this translation helpful? Give feedback.
-
I have a
ReorderableListView
and it is possible to sort items by dragging and dropping them, as one would expect. Reordering logic is implemented in theStateNotifier
managed by Riverpod, and the list is part of the state. Once sorting logic (a simpleList.removeAt
followed byList.insert
at the correct index) called byonReorder
is done, a new state with the new order is emitted.It works, but in release builds it results in flickering - for a split second after the drag and drop gesture, the old ordering is restored, and then immediately afterwards the new one is displayed - but the result is noticeable and pretty jarring. I assume this is because the new state (with correct order) from my provider (as received by
watch
) is available a bit later (maybe the next event loop cycle?) than immediately after theonReorder
is finished.This doesn't happen when a plain old
StatefulWidget
is used with a list in the state, and whensetState
is called inonReorder
- I assume this is because there is no 'delay' between updating the state and getting the new ordered list, as it is right there, immediately available.I guess I will be able to implement a workaround 'caching' the list in the state and reordering it, and syncing with the provider right afterwards, so that the flickering doesn't happen, but I'm wondering, is there anything better than this hack?
Beta Was this translation helpful? Give feedback.
All reactions