Flutter Provider:如何通知模型它包含的模型发生了变化?

Posted

技术标签:

【中文标题】Flutter Provider:如何通知模型它包含的模型发生了变化?【英文标题】:Flutter Provider: How to notify a model that a change happened on a model it contains? 【发布时间】:2020-08-01 12:26:39 【问题描述】:

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

This is what the app looks like

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

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();
        )
    );
  

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

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()
              )
          );
        
      )
    );
  

问题:

    在两个小部件中都有ChangeNotifierProviderConsumer 似乎很奇怪。我见过的例子都没有做到这一点。 让GroceryListSectionModel 监听所有GroceryItemModels 上的更改以将更改传播回树上是绝对错误的。我不知道如何正确扩展。

有什么建议吗?谢谢!

【问题讨论】:

【参考方案1】:

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

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

ItemModel 中的 complete() 函数位于 GroceryListSectionModel() 中,并带有来自当前列表索引的参数

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;
  


【讨论】:

以上是关于Flutter Provider:如何通知模型它包含的模型发生了变化?的主要内容,如果未能解决你的问题,请参考以下文章

Flutter Provider:拆分通知的正确方法

Flutter Provider select - 如何处理复杂的返回类型,如 List 或 Map

使用上下文和观察更改模型时,Flutter Provider 不会触发重建

Flutter Provider 和 sqflite 集成

Flutter中大型模型的状态应该如何管理?

Flutter - Provider - 如何根据 Provider 的值更改 UI?