Я работаю над проектом Flutter, где мне нужно удалить элементы из ListView.builder
и обновить список, чтобы отразить эти изменения. Однако я сталкиваюсь с проблемами, из-за которых список не обновляется правильно после удаления элемента. Вот упрощенная версия моего кода:
class List extends StatefulWidget {
@override
_ListState createState() => _ListState();
}
class _ListState extends State<List> {
List<Map<String, dynamic>> taskData = [
{'id': 1, 'name': 'List 1', 'status': 'Pending'},
{'id': 2, 'name': 'List 2', 'status': 'Completed'},
// More tasks...
];
void removeList(int taskId) {
setState(() {
taskData.removeWhere((element) => element['id'] == taskId);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Task List')),
body: ListView.builder(
itemCount: taskData.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(taskData[index]['name']),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => removeList(taskData[index]['id']),
),
);
},
),
);
}
}
Когда я удаляю задачу с помощью кнопки удаления, значок ListView.builder
не обновляется автоматически, чтобы отразить изменения. Мне приходится отойти от экрана и вернуться, чтобы увидеть обновленный список.
Использование setState для обновления списка. Обеспечение правильного обновления списка в методе setState. Использование уникальных ключей для элементов списка.
Как я могу гарантировать, что ListView.builder автоматически обновляется при удалении элемента? Существуют ли какие-либо передовые методы или альтернативные подходы для достижения этой цели во Flutter?
class List extends StatefulWidget {
@override
_ListState createState() => _ListState();
}
class _ListState extends State<List> {
List<Map<String, dynamic>> taskData = [
{'id': 1, 'name': 'List 1', 'status': 'Pending'},
{'id': 2, 'name': 'List 2', 'status': 'Completed'},
// More tasks...
];
void removeTask(int taskId) {
setState(() {
taskData.removeWhere((element) => element['id'] == taskId);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Task List')),
body: ListView.builder(
itemCount: taskData.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(taskData[index]['name']),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => removeTask(taskData[index]['id']),
),
);
},
),
);
}
}
Что изменилось в этом коде?
Я обновил метод removeTask
, чтобы удалить задачу из taskData
с помощью removeWhere
. Затем я позвонил removeTask
с правильным идентификатором задачи при нажатии кнопки удаления.
Мой ответ будет на последнюю часть вопроса, о лучших практиках и альтернативных подходах.
Согласно документации, не рекомендуется выполнять какие-либо вычисления в методе setState()
, поскольку это может стать причиной некачественного пользовательского интерфейса.
removeWhere
в данном контексте является «вычислением», поскольку оно имеет O(N)
Тим-сложность и может существенно повлиять на производительность. Итак, возможно, стоит пожертвовать немного местами памяти и функционально сделать что-то вроде этого:
void removeTask(int taskId) {
final newList =
ListData.where((element) => element['id'] != taskId).toList();
setState(() {
ListData = newList;
});
}
В этом случае никаких вычислений внутри нет setState
Но на мой взгляд, в данном случае лучше использовать ValueNotifier:
class _HomePageState extends State<_HomePage> {
// Create ValueNotifier instead simple list
ValueNotifier<List<Map<String, dynamic>>> ListData = ValueNotifier([
{'id': 1, 'name': 'List 1', 'status': 'Pending'},
{'id': 2, 'name': 'List 2', 'status': 'Completed'},
// More tasks...
]);
// Modify this method so there is no more setState, only updating value
// of ValueNotifier
void removeTask(int taskId) {
ListData.value =
ListData.value.where((element) => element['id'] != taskId).toList();
}
@override
Widget build(BuildContext context) {
return Scaffold(
// Wraps ListView.builder in ValueListenableBuilder, which will react
// on ListData changes.
// It may not have much sense here, but in comples widgets,
// you can use this widget down in nested tree of widgets, and changes in it
// will not affect outer tree
body: ValueListenableBuilder(
valueListenable: ListData,
builder: (context, value, child) => ListView.builder(
itemCount: value.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(value[index]['name']),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () => removeTask(value[index]['id']),
),
);
},
),
),
);
}
}
Текущая версия кода никогда не будет работать и, к сожалению, не подлежит компиляции.
taskData
должно бытьListData
иremoveList
должно бытьremoveTask
, я прав?