在flutter开发中,我们对State来进行管理,通过State的改变,来更新界面的刷新,状态管理分为两大类:
1.短时状态管理(定时器,动画效果等)
2.应用状态管理(用户信息,购物车等)
● 共享状态管理flutter官方提供了两种方案:
1. InheritedWidget
2. Provider(Provider是目前官方推荐的全局状态管理工具)
一、InheritedWidget 用法示例:
1. InheritedWidget 用法
① 继承 InheritedWidget
② 实现updateShouldNotify方法
③ 定义一个需要共享的状态,如counter;
④ 通过静态对象拿到对象,如counter;
InheritedWidget 写法如下:
class FCounterWidget extends InheritedWidget {
// 1.共享的数据
final int counter;
// 2.定义构造方法
FCounterWidget({this.counter, Widget child}): super(child: child);
// 3.获取组件最近的当前InheritedWidget
static FCounterWidget of(BuildContext context) {
// 沿着Element树, 去找到最近的HYCounterElement, 从Element中取出Widget对象
return context.dependOnInheritedWidgetOfExactType();
}
// 4.绝对要不要回调State中的didChangeDependencies
// 如果返回true: 执行依赖当期的InheritedWidget的State中的didChangeDependencies
@override
bool updateShouldNotify(FCounterWidget oldWidget) {
return oldWidget.counter != counter;
}
}
DEMO用法:
class InheritedWidgetPage extends StatefulWidget {
@override
_InheritedWidgetPageState createState() => _InheritedWidgetPageState();
}
class _InheritedWidgetPageState extends State<InheritedWidgetPage> {
int _counter = 100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget"),
),
body: FCounterWidget(
counter: _counter,
child: Center(
child: Column(
children: <Widget>[
Text("当前计数: $_counter", style: TextStyle(fontSize: 30),),
],
),
),
),
floatingActionButton:FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
_counter += 1;
});
},
),
);
}
}
二、Provider 用法示例:
2.Provider 用法 [官方推荐]
前提: 在项目中添加依赖 provider: ^4.1.2
① 创建需要共享的数据
② 在main函数中ChangeNotifierProvider
③ 使用共享的数据
核心代码如下:
//1.创建自己要共享的数据
class ContentViewModel extends ChangeNotifier {
int _counter = 100;
int get counter => _counter;
set counter(int value) {
_counter = value;
notifyListeners();
}
}
//2.在应用的main函数中使用ChangeNotifierProvider
void main() {
runApp(
ChangeNotifierProvider(
create: (ctx) => ContentViewModel(),
child: MyApp(),
)
);
}
//3.在其他位置使用共享的数据
class ProviderWidget extends StatefulWidget {
@override
_ProviderWidgetState createState() => _ProviderWidgetState();
}
class _ProviderWidgetState extends State<ProviderWidget> {
@override
Widget build(BuildContext context) {
int counter = Provider.of<ContentViewModel>(context).counter;
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget"),
),
body: Text("当前计数: $counter", style: TextStyle(fontSize: 30)),
floatingActionButton:Consumer<ContentViewModel>(
builder:(context,contentVM,child) {
return FloatingActionButton(
child: Icon(Icons.adb),
onPressed: () {
contentVM.counter +=1;
},
);
}
)
);
}
}
int counter = Provider.of<ContentViewModel>(context).counter;这种共享数据的方式不好,写法不推荐,一般更推荐下面的写法:
class _ProviderWidgetState extends State<ProviderWidget> {
@override
Widget build(BuildContext context) {
int counter = Provider.of<ContentViewModel>(context).counter;
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget"),
),
body: Consumer<ContentViewModel>(
builder:(context,contentVM,child) {
return Text("当前计数: $counter", style: TextStyle(fontSize: 30));
}
),
floatingActionButton:Consumer<ContentViewModel>(
builder:(context,contentVM,child) {
return FloatingActionButton(
child: Icon(Icons.adb),
onPressed: () {
contentVM.counter +=1;
},
);
}
)
);
}
}
Provider.of(context) VS Consumer 对比:
① 当Provider.of(context)中的数据发生改变时, Provider.of(context)所在的Widget将整个build方法重新构建;
① Consumer方法不会执行整个build,只会重新执行Consumer里面的build,此外Consumer方法里面的child也可以优化控件是否重新构建。所以一般项目开发中用Consumer居多
如上述floatingActionButton的代码可优化为如下代码:
///第一种优化方案 Icon 不需要重新构建
floatingActionButton:Consumer<ContentViewModel>(
builder:(context,contentVM,child) {
return FloatingActionButton(
child: child,
onPressed: () {
contentVM.counter +=1;
},
);
},
child:Icon(Icons.adb),
),
///第二种优化方案 FloatingActionButton 也不需要重新构建
floatingActionButton:Selector<ContentViewModel,ContentViewModel>(
selector: (context,contentVM) => contentVM,
//是否重新构建 true 是 false 否
shouldRebuild: (prev,next) => false,
builder:(context,contentVM,child) {
return FloatingActionButton(
child: child,
onPressed: () {
contentVM.counter +=1;
},
);
},
child:Icon(Icons.adb),
),
上述情况仅涉及一个ContentViewModel需要共享,但实际开发中有很多需要共享,比如用户信息,购物车,偏好设置等,假如我们又有一个用户信息的UserViewModel
,所以此时main函数creaet方法需要优化为如下代码:
void main() {
final userInfo = UserInfo("allison", "女", 18);
runApp(
//注册多个provider
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => ContentViewModel()),
ChangeNotifierProvider(create: (context) => UserViewModel(userInfo)),
],
),
);
//注册单个通知
// runApp(
// ChangeNotifierProvider(
// create: (ctx) => ContentViewModel(),
// child: MyApp(),
// )
// );
}
这样我们就可以实现多个provider的注册了,实现数据的共享。
其实像类似Consumer
还有一些高级的用法,如:Consumer2
,Consumer3
,Consumer4
,Consumer5
,Consumer6
等一些高级函数,比如:Consumer2
可以实现两个viewModel
数据拼接的展示,如:
例:想要展示ContentViewModel
和 UserViewModel
的数据,可使用Consumer2
,代码如下:
// Consumer常规用法
Consumer<UserViewModel>(
builder: (context,userVM,child) {
return Text("当前计数: ${userVM.user.name}", style: TextStyle(fontSize: 30),);
},
)
//Consumer2高级用法
Consumer2<UserViewModel,ContentViewModel>(
builder: (context,userVM,contentVM,child) {
return Text("名字: ${userVM.user.name} 当前计数:${contentVM.counter}",
style: TextStyle(fontSize: 30),);
},
)
更多高阶用法,期待后续发现中。
DEMO下载