1.1 分类
1.1.1 短时状态Ephemeral state
StatefulWidget对应的State类自己管理即可,Widget树中的其它部分并不需要访问这个状态。
例如;简单计数器counter,PageView组件记录当前的页面,动画记录当前的进度
1.2.2 应用状态App state
对状态进行统一的管理和应用
1.2 选择
选择能够减少麻烦的方式
2 共享状态管理
2.1. InheritedWidget
定义一个共享数据的InheritedWidget,需要继承自InheritedWidget
定义了一个of方法,该方法通过context开始去查找祖先的HYDataWidget(可以查看源码查找过程)
updateShouldNotify方法是对比新旧HYDataWidget,是否需要对更新相关依赖的Widget
class HYDataWidget extends InheritedWidget {
finalint counter;
HYDataWidget({this.counter, Widget child}): super(child: child);
static HYDataWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType();
}
@override
bool updateShouldNotify(HYDataWidget oldWidget) {
returnthis.counter != oldWidget.counter;
}
}
创建HYDataWidget,并且传入数据
class HYHomePage extends StatefulWidget {
@override
_HYHomePageState createState() => _HYHomePageState();
}
class _HYHomePageState extends State<HYHomePage> {
int data = 100;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("InheritedWidget"),
),
body: HYDataWidget(
counter: data,
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
HYShowData()
],
),
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
data++;
});
},
),
);
}
}
2.2. Provider
2.2.1. Provider的使用
一,创建自己的ChangeNotifier
class CounterProvider extends ChangeNotifier {
int _counter = 100;
intget counter {
return _counter;
}
set counter(int value) {
_counter = value;
notifyListeners();
}
}
二,Widget Tree中插入ChangeNotifierProvider
void main() {
runApp(ChangeNotifierProvider(
create: (context) => CounterProvider(),
child: MyApp(),
));
}
三,首页中使用Consumer引入和修改状态
body中使用Consumer,Consumer需要传入一个builder回调函数
在floatingActionButton中使用Consumer
class HYHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("列表测试"),
),
body: Center(
child: Consumer<CounterProvider>(
builder: (ctx, counterPro, child) {
return Text("当前计数:${counterPro.counter}", style: TextStyle(fontSize: 20, color: Colors.red),);
}
),
),
floatingActionButton: Consumer<CounterProvider>(
builder: (ctx, counterPro, child) {
return FloatingActionButton(
child: child,
onPressed: () {
counterPro.counter += 1;
},
);
},
child: Icon(Icons.add),
),
);
}
}
四,创建一个新的页面,在新的页面中修改数据
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第二个页面"),
),
floatingActionButton: Consumer<CounterProvider>(
builder: (ctx, counterPro, child) {
return FloatingActionButton(
child: child,
onPressed: () {
counterPro.counter += 1;
},
);
},
child: Icon(Icons.add),
),
);
}
}
2.2.2 Selector的选择
Consumer存在的弊端
-
比如当点击了floatingActionButton时,我们在代码的两处分别打印它们的builder是否会重新调用;
-
我们会发现只要点击了floatingActionButton,两个位置都会被重新builder;
-
但是floatingActionButton的位置有重新build的必要吗?没有,因为它是否在操作数据,并没有展示;
-
如何可以做到让它不要重新build了?使用Selector来代替Consumer
floatingActionButton: Selector<CounterProvider, CounterProvider>( selector: (ctx, provider) => provider, shouldRebuild: (pre, next) => false, builder: (ctx, counterPro, child) { print("floatingActionButton展示的位置builder被调用"); return FloatingActionButton( child: child, onPressed: () { counterPro.counter += 1; }, ); }, child: Icon(Icons.add), ),
Selector和Consumer对比,不同之处主要是三个关键点:
-
关键点1:泛型参数是两个
-
泛型参数一:我们这次要使用的Provider
-
泛型参数二:转换之后的数据类型,比如我这里转换之后依然是使用CounterProvider,那么他们两个就是一样的类型
-
-
关键点2:selector回调函数
-
转换的回调函数,你希望如何进行转换
-
S Function(BuildContext, A) selector
-
我这里没有进行转换,所以直接将A实例返回即可
-
-
关键点3:是否希望重新rebuild
-
这里也是一个回调函数,我们可以拿到转换前后的两个实例;
-
bool Function(T previous, T next);
-
因为这里我不希望它重新rebuild,无论数据如何变化,所以这里我直接return false;
2.2.3 MultiProvider
在开发中,我们需要共享的数据肯定不止一个,并且数据之间我们需要组织到一起,所以一个Provider必然是不够的,需增加一个新的ChangeNotifier
import'package:flutter/material.dart'; class UserInfo { String nickname; int level; UserInfo(this.nickname, this.level); } class UserProvider extends ChangeNotifier { UserInfo _userInfo = UserInfo("why", 18); set userInfo(UserInfo info) { _userInfo = info; notifyListeners(); } get userInfo { return _userInfo; } }
-