前端开发进阶篇——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)