Как удалить элементы из ListView.builder и обновить список во Flutter

Я работаю над проектом 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?

Текущая версия кода никогда не будет работать и, к сожалению, не подлежит компиляции. taskData должно быть ListData и removeList должно быть removeTask, я прав?

Sameri11 22.07.2024 14:57
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
78
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий
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']),
            ),
          );
        },
      ),
    );
  }
}

Что изменилось в этом коде?

Anu D 22.07.2024 15:29

Я обновил метод removeTask, чтобы удалить задачу из taskData с помощью removeWhere. Затем я позвонил removeTask с правильным идентификатором задачи при нажатии кнопки удаления.

Mansoor Malik 22.07.2024 15:40

Мой ответ будет на последнюю часть вопроса, о лучших практиках и альтернативных подходах.

Согласно документации, не рекомендуется выполнять какие-либо вычисления в методе 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']),
              ),
            );
          },
        ),
      ),
    );
  }
}

Другие вопросы по теме