flutter 状态管理
本文最后更新于:2022年4月22日 上午
flutter 状态管理
InheritedWidget
通过 InheritedWidget
可以实现状态管理,多个组件共享数据
1. 创建状态
步骤
- 继承
InheritedWidget
类 - 重写
updateShouldNotify
方法 - 编写
XXX.of()
方法 - 编写需要共享的数据,且在构造函数中接收
child
和 需要共享状态的参数
// 编写状态
class UserinfoState extends InheritedWidget {
final String username; // 共享的数据
UserinfoState({required Widget child, required this.username}) : super(child: child);
static UserinfoState of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType()!;
}
@override
bool updateShouldNotify(UserinfoState oldWidget) {
// 当两个值不一致的返回 true ,会执行 didChangeDependencies() 方法
return oldWidget.username != username;
}
}
2. 在共同的父组件共享状态
在父组件中使用状态 Widget
,且将 状态的值传入,将需要使用状态的子组件置于 child
中
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
String username = 'flutter';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: UserinfoState(
username: username,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [Text('需要共享的值:$username'), Child1(), Child2()],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => {setState(() => (username = username == 'flutter' ? 'dart' : 'flutter'))},
child: Icon(Icons.change_circle_outlined),
),
);
}
}
3. 子组件消费数据
通过 XXX.of(context)
来取到状态
class Child1 extends StatelessWidget {
const Child1({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final username = UserinfoState.of(context).username;
return Container(color: Colors.red, child: Text('child1: $username'));
}
}
class Child2 extends StatelessWidget {
const Child2({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final username = UserinfoState.of(context).username;
return Container(color: Colors.green, child: Text('child2: $username'));
}
}
Provider
Provider
是flutter
官方推出的一个状态管理库,也有 redux
,mobx
,bloc
等状态管理库,但是在官方推出了状态管理库后,基本上都转向了官方库。但是在 getX
推出后,情况发现一些改变,总之,Provider
是学习 flutter
值得学习的一个库。
添加到项目依赖
flutter pub add provider
1. 创建全局容器
在runApp
中 使用 MultiProvider
main() => runApp(
// 全局只有一个状态
// ChangeNotifierProvider(
// create: (ctx) => CounterStore(),
// child: MyApp(),
// ),
// 全局拥有多个需要共享的状态 MultiProvider
MultiProvider(
providers: [
ChangeNotifierProvider(create: (ctx) => CounterStore()),
ChangeNotifierProvider(create: (ctx) => TokenStore()),
],
child: MyApp(),
),
);
2. 创建状态
- 继承自
ChangeNotifier
- 编写
get/set
set
中值被修改后调用notifyListeners()
// 全局状态1 编写需要共享的状态
class CounterStore extends ChangeNotifier {
int _counter = 0;
set counter(int newCounter) {
_counter = newCounter;
notifyListeners(); // 设置的时候需要发送通知
}
// 该名称为使用时的名称,
int get counter => _counter;
}
// 全局状态2
class TokenStore extends ChangeNotifier {
String _token = ''; // 内部的
String get token => _token;
set token(newToken) {
_token = newToken;
notifyListeners();
}
// 修改token
void changeToken(String newToken) => token = newToken;
}
3. 消费数据(状态)
Provier
消费数据的方式在多次更新更新后有很多种
context.watch
context.watch<T>()
,widget
能够监听到 T
类型的 provider
发生的改变。
class Child2 extends StatelessWidget {
const Child2({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
// 获取全局状态
child: Text('token: ${context.watch<TokenStore>().token}')
);
}
}
context.read
该方法在消费状态的时候不会对状态产生依赖
但是该方法不能在 StatelessWidget
和 StatefulWidget
的build
方法中使用,除此之外的任何地方都可以
@override
void initState() {
super.initState();
// 获取全局方式
// 在 生命周期 组件构建完成的下一帧 请求数据存入全局 store,如果直接直接存会报错,文档有对应说明
Future.microtask(() => context.read<TokenStore>().changeToken('newToken 12532535'));
}
context.select
该方法可以将状态 进行一些转换
runApp(
Builder(builder: (context) {
final title = context.select<Movie?, String>((movie) => movie?.title);
if (title == null) Text('no Movie found');
return Text(title);
}),
);
Provider.of(context)
通过指定泛型 Provider.of<XxxState>(context)
的方式来消费数据,
当可选参数 listen: false
和 context.read
的作用相似
当可选参数 listen: true
或不传时和 context.watch
的作用相似
class Child1 extends StatelessWidget {
const Child1({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
// 获取全局状态
final counter = Provider.of<CounterStore>(context, listen: false).counter; // 获取 全局的状态
return Container(color: Colors.red, child: Text('child1: $counter'));
}
}
以上几种都是较为简单的使用方式,且代码阅读性强
Consumer
可优化子组件的 build
child: Consumer<CounterStore>(
builder: (context, value, child) {
return FloatingActionButton(
onPressed: () => value.counter += 1,
child: child, // 不会重新构建 icon
);
},
child: Icon(Icons.change_circle_outlined)
)
Consumerx
如果对多个数据有依赖可以通过 Consumer[2-6]
的方式来添加多个依赖,至多多个,同事,builder
方法中会依次传入对应的状态
child: Consumer2<CounterStore, TokenStore>(
builder: (context, value, value2, child) {
return FloatingActionButton(
onPressed: () => value.counter += 1,
child: child, // 不会重新构建 icon
);
},
child: Icon(Icons.change_circle_outlined)
),
Selector
可显示指定是否重新build
,且可以对状态进行转换
通过 shouldRebuild
函数的返回值来决定是否重新执行 builder
child: Selector<CounterStore, CounterStore>(
selector: (p0, p1) => p1,
shouldRebuild: (prev, next) => false, // 状态更新时不重新构建依赖
builder: (context, value, child) {
print('重新 构建'); // 只有第一次会打印,后续更新状态不会打印
return FloatingActionButton(
onPressed: () => value.counter += 1,
child: Icon(Icons.change_circle_outlined), // 不会重新构建 icon
);
},
),
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处。