Flutter 使用 Provider 管理状态的实践记录

date
May 24, 2021
slug
practice-record-using-provider-in-flutter-to-manage-state
status
Published
summary
通过全局的 Provider 管理文章的数据,当发生更新时,直接 Rebuild 对应的 Widget 即可
tags
Flutter
type
Post

一、背景

在做一个自用的 TinyTiny RSS 阅读器,其中将文章列表部分拆分成了三个模块以减少嵌套。
同时,针对于单篇文章需要提供点击文章和修改标题颜色、标记已读和未读、收藏和取消收藏的功能;因此涉及到子组件修改父组件的参数,所以需要在父组件提供一个方法用于修改,大致代码如下:
  • UnreadPage:页面父组件
  • FeedItem:Feed 流子组件
  • ArticleItem:article 流子组件
可以看到这样的结构比较繁琐,而且必须要使用 StatefulWidget 用于更新状态;
因此,想到通过全局的 Provider 管理文章的数据,当发生更新时,直接 Rebuild 对应的 Widget 即可。

二、示例一:文章流 & 交互

2.1 建立 Model

该 Model 主要提供更新文章数据,更新阅读和星标状态等功能。

2.2 列表构建

这部分主要是如何去使用 Provider 提供的数据:

2.2.1 注册 Model

这个是正常的注册方式,但是有时候会有很多个 Model 需要处理,因此可以处理成这样:

2.2.2 如何实现列表构建(数据获取和消费)

状态获取有三种方式:
  • Provider.of 方法:Provider.of<Model>(context, listen: false)
  • Consumer Widget:Consumer(builder: (context, Model provider, _) => Text('Value: ${provider.data}'))
  • Selector Widget:
每一次数据更新后,对应使用相关数据的 Widget 都会触发 Rebuild,而上述三种中后两种都可以实现精确地控制刷新粒度;
而 Selector 尤其适用于列表构建、针对单条数据单独触发 UI 刷新和管理是否需要刷新 UI,示例:
其中,当 shouldRebuild 设置为 false,则代表就算状态更新了该列表项也不会触发 UI 刷新;shouldRebuild 默认为 prev ≠ next
 
另外,可以进一步优化列表,在外面包一层 Selector,并且设置 shouldRebuild: (prev, next) => false,这样就禁止了因为状态更新导致整个列表重新刷新 UI 的可能。

2.3 已读和收藏

针对单篇文章的已读和收藏状态刷新也是一样,通过 Selector 更新:
 

2.4 Hot Reload 带来的一些问题

另外需要注意的一点是,有时候 Model 注册使用了ChangeNotifierProvider.value 会导致调试时热更新让状态数据丢失。
解决方案是使用下述方式注册

三、示例二:Tabbar 状态更新

3.1 定义 Model 属性

3.2 状态获取和更新

3.3 切换 Tab 时的一些影响

目前默认情况下切换 Tab 会导致前一页的状态丢失,因此需要使用一些方式进行处理:
在需要保持状态的页面添加with AutomaticKeepAliveClientMixin ,同时添加bool _wantKeepAlive = true
这样,从该页面切换出去,就不会丢失状态了。
但是,与此同时还带来一个问题,有时候我们需要更新页面的状态用以展示新的数据,如果一直强制保持,会导致页面无法展示出新的数据。
针对这个问题,可以通过定义wantKeepAlive,并通过方法updateKeepAlive来修改当前页面是否需要保持状态。