在flutter中,如果我们的应用足够简单,数据流动的方向和顺序是清晰的,我们只需要将数据映射成视图就可以了。作为声明式的框架,Flutter 可以自动处理数据到渲染的全过程,通常并不需要状态管理。
但,随着产品需求迭代节奏加快,项目逐渐变得庞大时,我们往往就需要管理不同组件、不同页面之间共享的数据关系。当需要共享的数据关系达到几十上百个的时候,我们就很难保持清晰的数据流动方向和顺序了,导致应用内各种数据传递嵌套和回调满天飞。在这个时候,我们迫切需要一个解决方案,来帮助我们理清楚这些共享数据的关系,于是状态管理框架便应运而生。
下面来了解一下如何使用Provider进行状态管理,使用步骤如下:
1、首先安装Provider
dependencies:
flutter:
sdk: flutter
provider: 3.0.0+1 #provider依赖
2、将需要共享的状态进行封装:
//定义需要共享的数据模型,通过混入ChangeNotifier管理听众
class CounterModel with ChangeNotifier {
int _count = 0;
//读方法
int get counter => _count;
//写方法
void increment() {
_count++;
notifyListeners();//通知听众刷新
}
}
需要记忆点的知识点,需要定义一个类,类的属性和方法就是需要共享的状态,这个类需要混入ChangeNotifier。这个类能够帮助我们管理所有依赖资源封装类的听众。当资源封装类调用 notifyListeners 时,它会通知所有听众进行刷新。
3、将封装的状态放在组件最高层,因为 Provider 实际上是 InheritedWidget 的语法糖,所以通过 Provider 传递的数据从数据流动方向来看,是由父到子(或者反过来),所以一般就是把资源放到更高的层级。需要注意的是是如何放。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
//通过Provider组件封装数据资源
return ChangeNotifierProvider.value(
value: CounterModel(),//需要共享的数据资源
child: MaterialApp(
home: FirstPage(),
)
);
}
}
可以看到,既然 Provider 是 InheritedWidget 的语法糖,因此它也是一个 Widget。所以,我们直接在 MaterialApp 的外层使用 Provider 进行包装,就可以把数据资源依赖注入到应用中,这里需要注意的是,由于封装的数据资源不仅需要为子 Widget 提供读的能力,还要提供写的能力,因此我们需要使用 Provider 的升级版 ChangeNotifierProvider。而如果只需要为子 Widget 提供读能力,直接使用 Provider 即可。
4、在子组件中通过of方法获取属性与方法,部署状态。
//第一个页面,负责读数据
class FirstPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
//取出资源
final _counter = Provider.of<CounterModel>(context);
return Scaffold(
//展示资源中的数据
body: Text('Counter: ${_counter.counter}'),
//跳转到SecondPage
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.of(context).push(MaterialPageRoute(builder: (context) => SecondPage()))
));
}
}
//第二个页面,负责读写数据
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
//取出资源
final _counter = Provider.of<CounterModel>(context);
return Scaffold(
//展示资源中的数据
body: Text('Counter: ${_counter.counter}'),
//用资源更新方法来设置按钮点击回调
floatingActionButton:FloatingActionButton(
onPressed: _counter.increment,
child: Icon(Icons.add),
));
}
}
以上便是使用Provider对flutter进行状态管理的过程,再看一个案例,如图:
图上是两个兄弟组件,我在在一个组件中展示数据,在另一个组件中点击按钮数据发生改变。代码如下:
import 'package:flutter/material.dart';
class Datashare extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [App1(), App2()],
);
}
}
class App1 extends StatelessWidget {
// 获取属性与方法
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: Colors.yellow,
child: Text("111"),
);
}
}
class App2 extends StatelessWidget {
// 获取属性与方法
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: Colors.redAccent,
child: FlatButton(child: Text("加一"),color: Colors.redAccent,onPressed: ()=>{
})
);
}
}
按照第一个案例的方法,我们对上面的代码进行改造:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// 封装状态:
class CountContainer with ChangeNotifier {
int _count = 0;
int get counter => _count;
void increment() {
_count++;
notifyListeners();
}
}
// class Datashare extends StatelessWidget {
// @override
// Widget build(BuildContext context) {
// return Row(
// children: [App1(), App2()],
// );
// }
// }
// 将封装的装填放到顶层组件;
class Datashare extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: CountContainer(),
child: Row(
children: [App1(), App2()],
));
}
}
class App1 extends StatelessWidget {
// 获取属性与方法
@override
Widget build(BuildContext context) {
final _counter = Provider.of<CountContainer>(context);
print(_counter);
return Container(
width: 100,
height: 100,
color: Colors.yellow,
child: Text('${_counter._count}'),
);
}
}
class App2 extends StatelessWidget {
// 获取属性与方法
@override
Widget build(BuildContext context) {
final _counter = Provider.of<CountContainer>(context);
return Container(
width: 100,
height: 100,
color: Colors.redAccent,
child: FlatButton(
child: Text("加一"),
color: Colors.redAccent,
onPressed: () => {
_counter.increment()
}));
}
}
需要注意的是这一句:_counter = Provider.of<CountContainer>(context),首先注意调用的地方,是在build函数中,因为在build函数中可以访问到context,然后是of函数的返回值的类型是封装的数据状态,此处不能省略。
上面的案例我们只定义了一个状态,如果是多个状态呢?
其实也不难。接下来,我就按照封装、注入和读写这 3 个步骤,与你介绍多个数据状态的共享。
此时我么需要MultiProvider,我们修改上面的代码,注入一个只读的数字,这个数字只做展示,局部代码如下:
class Datashare extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(providers: [
Provider.value(value: 30.0),
ChangeNotifierProvider.value(
value: CountContainer(),
)
],
child: Row(
children: [App1(), App2()],
));
}
}
class App1 extends StatelessWidget {
// 获取属性与方法
@override
Widget build(BuildContext context) {
final _counter = Provider.of<CountContainer>(context);
final textSize = Provider.of<double>(context);
print(_counter);
return Container(
width: 100,
height: 100,
color: Colors.yellow,
child: Text('${_counter._count}----$textSize'),
);
}
}
我们只是修改了注入时的用法,增加了读取的用法,这里需要注意的是注入时Provider.value注入的是只读数据,不能被修改。
以上便是flutter中利用Provider进行状态管理的案例,希望对你有所帮助。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。