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

Posted

技术标签:

【中文标题】Flutter中大型模型的状态应该如何管理?【英文标题】:How should I manage the state of a large model in Flutter? 【发布时间】:2021-11-15 13:10:05 【问题描述】:

我正在编写我的第一个 Flutter 应用程序,并在各种状态管理解决方案中苦苦挣扎。我决定从 Provider 开始,但我正在考虑切换到 BLoC。到目前为止,我发现的大多数示例都仅限于相对简单的事情,例如显示项目列表或响应某些按钮按下。就我而言,几乎所有应用程序都专注于设置大量数据。 (它基本上是一大堆表单,它们都在大数据结构的不同位上工作。)

目前,所有状态管理都放在一个提供程序类中,因为其中大部分都非常密切相关。例如,它的最大部分是一个项目列表,然后是该列表的一堆子集。应用程序中的大部分数据操作都在这些子集上。

一开始我并没有真正打算这样做,但我发现自己将实际使用提供程序的代码放置在非常接近顶层的位置,然后将数据向下传递到树中。它违背了 Provider 的全部观点,但它减少了重复的代码。例如,我的应用程序中的一个屏幕有一堆卡片,它们都包含非常相似的列表。它们之间的主要区别在于它们的内容来自不同的列表。所以为了减少重复代码,我尽可能将卡片和列表泛化,并将必要的数据向下传递。我还传递回调函数来处理诸如编辑项目或将其从列表中删除之类的事情。

这听起来像是一个不错的方法吗?我在树中传递状态和回调的事实对我来说就像是一种代码味道,但正如我所提到的,它导致重复代码更少。我有一个暴露很多东西的供应商这一事实呢?我的一部分感觉我应该将数据模型与提供者分开,然后有多个较小的提供者。如果我走那条路,BLoC 会比 Provider 更合适吗?如果我能够拆分单个提供商,是否会提高性能?拥有多个提供者或集团似乎会使保存更改变得复杂,但也许不会。你有没有发现这是一个问题?

编辑:我刚刚发现了这个问题,它谈到了为不同数据重用提供者:Flutter Provider. How to have multiple instances of the same provider type?

我以前真的没有这样想过,但这是我正在努力解决的一个重要部分。我有独立修改的不同列表。它是在多个地方(有时在同一个屏幕上)使用的相同小部件,如果我将我拥有的东西分解成更小的部分,提供程序中的功能也将是相同的。由于 Provider 基于类型而不是实例,因此在这种情况下可能无法正常工作。

【问题讨论】:

【参考方案1】:

我的第一个 Flutter 应用使用了一个 Provider 类,它可以工作,但它是一个巨大的混乱类,并触发了大量的 UI 更新。

目前我为数据模型使用“模型”文件夹,为提供者单例使用“服务”文件夹。

Models 拥有我所有的具有实用程序和突变方法的数据类。例如一个 MyCard 类来保存值加上 MyCard.fromJson() 和 .toJson() 用于序列化,也许还有一些例程,如 clear() 和 validate()。

Services 是我将 Providers 创建为单例的地方。例如,class MyCardService with ChangeNotifier 单例具有 List<MyCard> 来保存所有 MyCard。如果我希望 UI 小部件根据更改进行更新,例如在 MyCardService().loadCardsFromNetowrk() 完成时,我使用 Provider watchselect 在 build() 方法中访问该服务。如果我想访问 build() 链之外的数据,我可以使用单例模式访问它:MyCardService().myVariable。这消除了在 build() 链之外进行上下文查找的需要。

这为异步访问提供了很多控制。例如,您可以在 get 和 set 方法上有一个互斥锁,Future<bool> isInitialized 在您的服务尝试初始化后解析为 true 或 false。您可以在需要访问服务或 FutureBuilder() 的任何异步调用中等待这个未来

我还有一些非提供者服务单例,其他服务用于网络 API 访问和文件系统缓存等内容。

提供者服务主要由 UI 数据消费来组织。这样,notifyListeners() 不会为未修改的小部件触发 UI 重建(我的第一个单文件提供程序项目的一个主要问题是使用 select 而不是一个 watch 的一堆单独变量)。

在上面的例子中,Provider 单例包含一个 List 和所有在 List 上工作的方法。 MyCard 数据模型包含处理 MyCard 的所有方法。当我有一个编辑单个 MyCard 的小部件时,我使用一个有状态的小部件,它在编辑完成时执行 MyCardService().save(myCard)。这也有助于隔离 UI 树的重建。

【讨论】:

感谢@Pat9RB。您是否有任何在同一模型上工作的单独服务?例如,如果您在 MyCard 旁边有一个 Foo 模型,以及一个包含 MyCard 和 Foo 列表的 Bar 模型,您是否可能有单独的服务来修改每个列表?关于单例 - 我一直在使用 Provider.of(context, listen: false) 来完成同样的事情。改用单例有什么好处吗? 我没有任何保存相同数据的服务,但我有它们级联。我确实有将多个模型作为一个单元提供的服务——比如 PersonService 可能包含模型:complexAddress、socialAccounts、txHistory 等,如果我想要这些都在一个视图中。我会有一个查看 PersonService 的“DisplayPerson”小部件。如果我有一个只有 txHistory 的单独视图,我会在它自己的 TxHistoryService 中使用 ShowTxHistory 小部件,当我希望两者都显示在同一个视图上时,将 DisplayPerson 和 ShowTxHIstory wigets 放在列或列表视图等中。 单例模型允许您使用没有 Flutter 上下文的服务来查找它,例如从另一个服务。但我最终几乎在不需要监听器连接的任何地方都使用它,因为它更干净,即MyService().doSomething()MyService().value vs context.read<MyService>().doSomething()Provider.of<MyService>(context, listen: false).value

以上是关于Flutter中大型模型的状态应该如何管理?的主要内容,如果未能解决你的问题,请参考以下文章

阿里宣布开源Flutter应用框架Fish Redux!

刚刚,阿里宣布开源Flutter应用框架Fish Redux!

如何仅在 Flutter 中的特定页面上更改状态栏颜色

只要状态没有被授予,如何一直询问位置权限-Flutter

Flutter 文本小部件

Flutter 状态管理 实践记录