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()
)
);
)
);
问题:
-
在两个小部件中都有
ChangeNotifierProvider
和Consumer
似乎很奇怪。我见过的例子都没有做到这一点。
让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 select - 如何处理复杂的返回类型,如 List 或 Map