Это сложная проблема, но я постараюсь ее объяснить.
В моем проекте используется база данных sqflite. Эта конкретная страница возвращает список отклоняемых виджетов в соответствии с данными в базе данных. Вот как я читаю данные:
class TaskListState extends State<TaskList> {
DBProvider dbProvider = new DBProvider();
Future<List<Task>> allTasks;
@override
void initState() {
allTasks = dbProvider.getAllTasks();
super.initState();
}
void update(){
setState(() {
allTasks = dbProvider.getAllTasks();
});
}
//build
}
Виджет TaskList возвращает страницу с FutureBuilder, которая строит ListView.builder с данными из базы данных. ListView создает закрываемые виджеты. При закрытии виджетов, которые можно закрыть, строка в базе данных обновляется, а данные снова считываются для обновления списка.
метод сборки для TaskListState
@override
Widget build(BuildContext context) {
return ListView(
physics: const AlwaysScrollableScrollPhysics(),
children: <Widget>[
//other widgets such as a title for the list
),
FutureBuilder(
future: allTasks,
builder: (context, snapshot){
if(snapshot.hasError){
return Text("Data has error.");
} else if (!snapshot.hasData){
return Center(
child: CircularProgressIndicator(),
);
} else {
return pendingList(Task.filterByDone(false, Task.filterByDate(Datetime.now, snapshot.data))); //filters the data to match current day
}
},
),
//other widgets
],
);
}
PendingList
Widget pendingList(List<Task> tasks){
//some code to return a Text widget if "tasks" is empty
return ListView.separated(
separatorBuilder: (context, index){
return Divider(height: 2.0);
},
itemCount: tasks.length,
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index){
return Dismissible(
//dismissible backgrounds, other non-required parameters
key: Key(UniqueKey().toString()),
onDismissed: (direction) async {
Task toRemove = tasks[index]; //save the dismissed task for the upcoming operations
int removeIndex = tasks.indexWhere((task) => task.id == toRemove.id);
tasks.removeAt(removeIndex); //remove the dismissed task
if(direction == DismissDirection.endToStart) {
rateTask(toRemove).then((value) => update()); //rateTask is a function that updates the task, it is removed from the list
}
if(direction == DismissDirection.startToEnd) {
dbProvider.update(/*code to update selected task*/).then((value) => update());
}
},
child: ListTile(
//ListTile details
),
);
},
);
}
Вот проблема (может быть, неправильная интерпретация, я все еще новичок):
Отклонение виджета по существу удаляет его из списка. После того, как пользователь закрывает задачу, она «визуально» удаляется из списка и вызывается метод update()
, который вызывает setState()
. Вызов setState()
заставляет FutureBuilder снова строить, но вызов dbProvider.getAllTasks()
не завершается к тому времени, когда FutureBuilder строит снова. Таким образом, FutureBuilder передает старый снимок, что заставляет ListView снова строить с помощью задачи, которая только что была отклонена. Это приводит к тому, что закрытый ListTile появляется на мгновение после закрытия, что выглядит жутко и неправильно.
Понятия не имею, как это исправить. Любая помощь будет оценена по достоинству.