像vue、react有对应的状态管理库,比如:pinia
、Redux
。同样flutter中也有状态管理库,但是flutter中的状态管理库很多,对于像我这样的新手来说这很难选择。因此只好选择官方库——Provider
因为是第一次学习,如果有哪些地方理解的不正确,麻烦指出。
参考:
官方文档
Provider是一个Flutter状态管理库,它是基于InheritedWidget的简单、可扩展且易于使用的解决方案。它帮助开发者在Flutter应用程序中管理和共享状态,以便于组件之间的通信和数据共享。
Provider的核心概念是"Provider"和"Consumer"。Provider是一个数据源,它包含要共享的数据,并将其提供给应用程序中的其他组件。Consumer是一个接收Provider数据的组件,它会自动订阅Provider,并在数据发生变化时进行更新。
使用Provider,开发者可以避免在组件树中手动传递数据。相反,它提供了一种便捷的方式来访问和更新共享数据。开发者只需要在应用程序的根部分创建一个Provider,并在需要访问共享数据的组件中使用Consumer来获取数据。
Provider还提供了一些高级功能,如多Provider的组合、跨组件的数据传递、数据变更通知、异步数据处理等。它还支持与其他状态管理库(如Redux)的集成,以满足更复杂的应用程序需求。
这里的话,我们只学习最基础的部分。高级功能等水平达到后,再学习
安装
flutter pub add provider
我们以数字新增为例
首先需要创建一个状态管理类,对于需要共享的状态进行统一管理
counter_state.dart
import 'package:flutter/cupertino.dart';
// 创建状态管理类
class CounterState with ChangeNotifier {
// 要共享的状态
int _count = 0;
// 获取状态
int get count => _count;
// 修改状态的方法
void increment() {
_count++;
// 通知使用该状态的widget
notifyListeners();
}
}
注意点:
//使用箭头函数简写
main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
//创建widget的唯一标识
const MyApp({Key? key}) : super(key: key);
//重写build方法
Widget build(BuildContext context) {
// ChangeNotifierProvider 会返回一个 ChangeNotifier 对象,它允许消费者在 CounterState 对象发生变化时收到通知。
return ChangeNotifierProvider(
// CounterState 对象会作为共享的状态实例被整个应用程序使用。
create: (context) => CounterState(),
child: const MaterialApp(
home: YcHomePage(),
),
);
}
}
或者
runApp(ChangeNotifierProvider(
create: (context) => CounterState(),
child: const MyApp(),
));
本质上是通过ChangeNotifierProvider
来为最父级的widget
来提供状态
//app的主页面
class YcHomePage extends StatelessWidget {
const YcHomePage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
// 从上下文中获取类型为 CounterState 的对象
final counter = Provider.of<CounterState>(context);
return Scaffold(
appBar: AppBar(
title: const Text('Provider状态管理'),
),
body: Center(
child: Column(
children: [
// 调用get方法获取值
Text("数值是:${counter.count}"),
// 用于占位
const SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () {
// 调用increment方法
counter.increment();
},
child: const Text("加1"))
],
)),
);
}
}
注意点:
避免产生副作用,我们在build
方法里通过 final counter = Provider.of
来获取实例。这里需要注意不要在build
方法里执行一些操作,比如数据请求。原因是:调用Provider.of
会重新运行其 build
方法,这会导致build
里面的某些操作又被执行了一次。,这就是副作用
解决方式是在initState
方法里来执行这些操作,initState
只会在初始化的时候被执行。
或者
class YcHomePage extends StatelessWidget {
const YcHomePage({Key? key}) : super(key: key);
Widget build(BuildContext context) {
// 掉接口获取数据,或者其他的操作
return Scaffold(
appBar: AppBar(
title: const Text('Provider状态管理'),
),
body: Center(
child: Consumer<CounterState>(
builder: (context,counterState,child){
return Column(
children: [
// 调用get方法获取值
Text("数值是:${counterState.count}"),
// 用于占位
const SizedBox(
height: 30,
),
ElevatedButton(
onPressed: () {
// 调用increment方法
counterState.increment();
},
child: const Text("加1"))
],
);
}
)
)
);
}
}
这种方式是通过Consumer
来创建一个消费者,然后在builder函数中通过上下文参数(context)来获取CounterState
对象。这样做的优点是可以直接在builder
函数中访问CounterState
对象,并且可以在builder
函数中进行一些额外的逻辑处理。
这种方式下,在build
里执行的操作不会在状态更新后再次执行。但是在builder
中的话,当状态更新后每次都会执行。这里可以自己使用print
打印一下日志试试。
局部使用与全局使用很相似,假设有两个子组件需要共享某个状态,这时你可以在这两个子组件的公共父组件上使用ChangeNotifierProvider
来提供状态。
如果需要共享的状态不多时,使用一个状态管理类是没有问题的。但是当状态过多时,都放在一个状态管理类里是不合适。这时应该建立不同的状态类,来管理不同类别的状态。这时后可以结合MultiProvider
main() {
runApp(MultiProvider(
providers: [
ChangeNotifierProvider<CounterState>(create: (context) => CounterState()),
// 其他的状态
],
child: const MyApp(),
));
}
关于获取到对应的状态实例,你可以使用final counter = Provider.of
,也可以使用
Consumer
,唯一需要注意的是类型不要填写错误了。
上面的例子中使用的都是ChangeNotifierProvider
,除了ChangeNotifierProvider
还有其他的Provider
ListenableProvider:
ChangeNotifierProvider:
ValueListenableProvider:
StreamProvider:
下面简单看一个ListenableProvider
,其他略,可自行百度
创建自定义状态类
class ListDataState with ChangeNotifier {
List<String> _listData = [];
List<String> get listData => _listData;
void addItem(String item) {
_listData.add(item);
notifyListeners();
}
void removeItem(String item) {
_listData.remove(item);
notifyListeners();
}
}
使用
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('列表数据状态管理'),
),
body: Consumer<ListDataState>(
builder: (context, listDataState, child) {
return ListView.builder(
itemCount: listDataState.listData.length,
itemBuilder: (context, index) {
final item = listDataState.listData[index];
return ListTile(
title: Text(item),
onTap: () {
listDataState.removeItem(item);
},
);
},
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Provider.of<ListDataState>(context, listen: false).addItem('New Item');
},
child: Icon(Icons.add),
),
);
}