前端开发进阶篇——Flutter状态管理Provider
1. 为什么需要状态管理
我们都知道一个应用程序随着功能的增加,将会有几十个页面,应用需要共享多处统一状态。如果一个页面状态更新,我们需要在多个页面进行状态同步更新。Flutter 其实就为我们提供了一种状态管理方式StatefulWidget,然而它仅仅适合用于在单个 Widget 内部维护其状态。当我们需要使用跨组件的状态时,StatefulWidget 将不再是一个好的选择,所以我们需要引用Provider插件,它是flutter官方推荐的状态管理方式。
2. Provider分类
Provider
一个Provider,通过委派一对Create和Dispose来管理维护所提供的状态值的生命周期。
它的构造方法是:Provider({Key key, @required Create<T> create, Dispose<T> dispose, bool lazy, TransitionBuilder builder, Widget child})。
其中Create<T> 方法是必须有的,用来创建所以侦听的对象。Dispose<T>用来销毁侦听对象。
ListenableProvider
ChangeNotifierProvider
是ChangeNotifier的子类,通常我们使用的是ChangeNotifierProvider 。ChangeNotifierProvider能够对子节点提供一个继承 、 混入、 实现 了 ChangeNotifier 的Model类。我们只需要在需要侦听的Model类 中 with ChangeNotifier ,然后在需要刷新状态的位置调用 notifyListeners ()即可。
ValueListenableProvider
ValueListenableProvider 用于提供实现了 继承、混入、实现 了 ValueListenable 的 Model类。是专门用于处理只有一个单一变化数据的 ChangeNotifier。
通过 ValueListenable 处理的Model类不再需要数据更新的时候调用 notifyListeners()。
StreamProvider
StreamProvider 专门用作提供(provide)侦听Stream,将其内容公开给child后代。它的主要用于是向大量窗口小部件提供Stream的内容。
FutureProvider
侦听Future组件,提供了一个 Future 给其子孙节点,并在 Future 完成时,通知依赖的子孙节点进行刷新。
3. Provider实战
主题切换功能
我们首先创建我们需要侦听状态的Model类,它不仅储存了我们的数据模型,而且还包含了更改数据的方法,并暴露出它想要暴露出的数据。这里我们让ThemelModel类混入了ChangeNotifier。我们在执行_update()方法时,调用 notifyListeners() 时,它会通知所有听众进行刷新。其代码如下:
import 'package:flutter/material.dart'; class ThemeModel with ChangeNotifier { ThemeData themeData; ThemeType currentType; ThemeModel(ThemeType type) { currentType = type; if (type == ThemeType.dark) { themeData = ThemeData.dark(); } else { themeData = ThemeData.light(); } } void reverse() { if (currentType == ThemeType.light) { _update(ThemeType.dark); } else { _update(ThemeType.light); } } void _update(ThemeType type) { currentType = type; if (type == ThemeType.light) { themeData = ThemeData.light(); } else { themeData = ThemeData.dark(); } notifyListeners(); } } enum ThemeType { light, dark, red }
接下来我们在 main 方法中注入监听全局数据ThemeModel类:
ChangeNotifierProvider<ThemeModel>(
create: (_) {
return ThemeModel(ThemeType.light);
},
ChangeNotifierProvider<ThemeModel>不仅能够提供数据供子孙节点使用,还可以在数据改变的时候通知所有听众刷新。
最后我们点击按钮操作事件,调用Provider.of<T>(context)来获取ThemeModel类reverse()方法,修改应用的主题模式,当主题发生改变时,并通过 Consumer<ThemeModel>获取数据状态改变修改模式提示字。关键代码如下:
FlatButton( child: Consumer<ThemeModel>(builder: (context, value, child) { String result = ''; if (value.currentType == ThemeType.dark) { result = '白天模式'; } else { result = '夜间模式'; } return Text(result); }), onPressed: () { Provider.of<ThemeModel>(context, listen: false).reverse(); }, )
运行截图如下(点击按钮可以实现夜间模式和白天模式互换):
商城菜单联动功能
我们需要实现通过点击左侧菜单栏,实现右上方子类的状态的修改,具体截图如下:
首先我们还是需要创建我们需要侦听状态的Model类
class ChildCategory with ChangeNotifier { List<CategoryGood> childCategoryList = []; getChildCategory(List<CategoryGood> list) { // childCategoryList = list; CategoryGood all = CategoryGood(); all.categoryId = 00; all.goodId = '00'; all.goodName = '全部'; childCategoryList = [all]; childCategoryList.addAll(list); notifyListeners(); } }
在主入口文件main函数中注入监听Model类,这次我们使用了ListenableProvider。
void main() { runApp(ListenableProvider<ChildCategory>( create: (_) => ChildCategory(), child: MyApp())); }
当我们从后台获取到商品分类时,第一次调用 Provider.of<ChildCategory>(context, listen: false).getChildCategory(list[0].categoryGood);初始化加载后左侧菜单栏显示。
void _getCategory() async { await request('CategoriesContent').then((value) { var data = json.decode(value.toString()); print(data); CategoryModel category = CategoryModel.fromJson(data); setState(() { list = category.data; }); Provider.of<ChildCategory>(context, listen: false) .getChildCategory(list[0].categoryGood); }); }
接下来我们需要在InkWell组件的onTap()方法中,实现通过点击左侧菜单,实现将其子类数据传递给监听Model类的getChildCategory( List <CategoryGood> list)方法,实现左侧子菜单数据的更新
onTap: () { setState(() { listIndex = index; }); var childList = list[index].categoryGood; Provider.of<ChildCategory>(context, listen: false) .getChildCategory(childList); },
然后我们通过Provider.of<ChildCategory>(context, listen: true).childCategoryList[index])获取更新完后左侧子菜单数据。
ListView.builder( scrollDirection: Axis.horizontal, itemCount: Provider.of<ChildCategory>(context, listen: true) .childCategoryList .length, itemBuilder: (context, index) { // print(Provider.of<ChildCategory>(context, listen: true) // .childCategoryList[index]); return _rightInkwel( Provider.of<ChildCategory>(context, listen: true) .childCategoryList[index]); }, )
以上是我在实践中用到的Provider实现的状态管理的例子,还在学习中,如有错误,请批评指正!
- 点赞
- 收藏
- 关注作者
评论(0)