首页 > 解决方案 > Flutter Provider:如何通知模型它包含的模型发生了变化?

问题描述

我开始通过使用 Provider 构建一个简单的 Todo 应用程序来学习 Flutter/Dart,但我遇到了一个状态管理问题。需要明确的是,我编写的代码有效,但似乎......错了。我找不到任何与我的案例相似的例子,足以让我理解解决问题的正确方法是什么。

这是应用程序的外观

这是一个按部分(“冷冻”、“水果和蔬菜”)划分的杂货清单。每个部分都有多个项目,并显示“已完成 x of y”进度指示器。每个项目在按下时都会“完成”。

GroceryItemModel看起来像这样:

class GroceryItemModel extends ChangeNotifier {
  final String name;

  bool _completed = false;

  GroceryItemModel(this.name);

  bool get completed => _completed;

  void complete() {
    _completed = true;
    notifyListeners();
  }
}

我在GroceryItem小部件中使用它,如下所示:

class GroceryItem extends StatelessWidget {
  final GroceryItemModel model;

  GroceryItem(this.model);

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider.value(
        value: model,
        child: Consumer<GroceryItemModel>(builder: (context, groceryItem, child) {
          return ListTile(
              title: Text(groceryItem.name),
              leading: groceryItem.completed ? Icon(Icons.check_circle, color: Colors.green) : Icon(Icons.radio_button_unchecked)
              onTap: () => groceryItem.complete();
        })
    );
  }
}

我想要的下一步是在一个部分中包含多个项目,它根据完成的项目数量来跟踪完整性。

GroceryListSectionModel看起来像这样:

class GroceryListSectionModel extends ChangeNotifier {
  final String name;
  List<GroceryItemModel> items;

  GroceryListSectionModel(this.name, [items]) {
    this.items = items == null ? [] : items;

    // THIS RIGHT HERE IS WHERE IT GETS WEIRD
    items.forEach((item) {
      item.addListener(notifyListeners);
    });
    // END WEIRD
  }

  int itemCount() => items.length;
  int completedItemCount() => items.where((item) => item.completed).length;
}

我在GroceryListSection小部件中使用它,如下所示:

class GroceryListSection extends StatelessWidget {
  final GroceryListSectionModel model;
  final ValueChanged<bool> onChanged;

  GroceryListSection(this.model, this.onChanged);

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider.value(
      value: model,
      child: Consumer<GroceryListSectionModel>(
        builder: (context, groceryListSection, child) {
          return Container(
              child: ExpansionTile(
                  title: Text(model.name),
                  subtitle: Text("${groceryListSection.completedItemCount()} of ${groceryListSection.itemCount()} completed"),
                  children: groceryListSection.items.map((groceryItemModel) =>
                      GroceryItem(groceryItemModel)).toList()
              )
          );
        }
      )
    );
  }
}

问题:

  1. ChangeNotifierProvider在两个小部件中都有 a和 a似乎很奇怪Consumer。我见过的例子都没有做到这一点。
  2. 监听所有更改以传播到树上绝对是错误的。我看不出它如何正确扩展。GroceryListSectionModelGroceryItemModels

有什么建议么?谢谢!

标签: flutterdartstate-managementflutter-provider

解决方案


这不是嵌套提供程序,但我认为在您的示例中这是更好的方法..

每个section ("Frozen", "Fruits and Veggies")仅定义一个 ChangeNotifierProvider

complete()来自 a的函数ItemModelGroceryListSectionModel()and 中,带有来自当前列表索引的参数

class GroceryListSection extends StatelessWidget {
  final GroceryListSectionModel model;
 // final ValueChanged<bool> onChanged;

  GroceryListSection(this.model);

  @override
  Widget build(BuildContext context) {

    return new ChangeNotifierProvider<GroceryListSectionModel>(
        create: (context) => GroceryListSectionModel(model.name, model.items),
        child: new Consumer<GroceryListSectionModel>(
        builder: (context, groceryListSection, child) {
          return Container(
              child: ExpansionTile(
                  title: Text(model.name),
                  subtitle: Text("${groceryListSection.completedItemCount()} of ${groceryListSection.itemCount()} completed"),
                  children: groceryListSection.items.asMap().map((i, groceryItemModel) => MapEntry(i, GroceryItem(groceryItemModel, i))).values.toList()
              )
          );
        }
      )
    );
  }
}

class GroceryItem extends StatelessWidget {
  final GroceryItemModel model;
  final int index;

  GroceryItem(this.model, this.index);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(model.name),
      leading: model.completed ? Icon(Icons.check_circle, color: Colors.green) : Icon(Icons.radio_button_unchecked),
      onTap: () => Provider.of<GroceryListSectionModel>(context, listen: false).complete(index),
    );

  }
}

class GroceryListSectionModel extends ChangeNotifier {
  String name;
  List<GroceryItemModel> items;

  GroceryListSectionModel(this.name, [items]) {
    this.items = items == null ? [] : items;
  }

  int itemCount() => items.length;
  int completedItemCount() => items.where((item) => item.completed).length;

  // complete Void with index from List items
  void complete(int index) {
    this.items[index].completed = true;
    notifyListeners();
  }

}

// normal Model without ChangeNotifier
class GroceryItemModel {
  final String name;
  bool completed = false;

  GroceryItemModel({this.name, completed}) {
    this.completed = completed == null ? false : completed;
  }

}

推荐阅读