https://zhuanlan.zhihu.com/p/392551608
在各种前端开发中,由于状态管理对于App的开发维护成本,性能等方面都起着至关重要的作用,所以选择合适的状态管理框架显得尤为重要。Flutter作为跨平台框架的后起之秀,背靠Google大树,短时间内开发者们在开源社区提供了多种状态管理框架。而Provider是官方推荐的状态管理方式之一,可用作跨组件的数据共享。本文对Provider框架的使用方法做了介绍,并在最后对主流的状态管理框架进行比较。
使用
Provider,通常使用ChangeNotifierProvider配合ChangeNotifier一起使用来实现状态的管理与Widget的更新。
其中 ChangeNotifier 是系统提供的,用来负责数据的变化通知。 ChangeNotifier 是一个类
ChangeNotifierProvider本质上其实就是Widget,它作为父节点Widget,可将数据共享给其所有子节点Widget使用或更新。
所以通常我们只需要三步即可利用Provider来实现状态管理。
1.创建混合或继承ChangeNotifier的Model,用来实现数据更新的通知并监听数据的变化。
2.创建ChangeNotifierProvider 或者用MultiProvider组合,用来声明Provider,实现跨组建的数据共享。
3.接收共享数据。
1、model部分
import 'package:flutter/material.dart';
class CountModel extends ChangeNotifier {
int _count;
CountModel() : _count = 1;
int get count => _count;
set count(int count) {
_count = count;
notifyListeners();
}
void add() {
this.count = _count+1;
}
void minus() {
this.count = _count-1;
}
}
model部分的写法:
1、 类
class 普通类 (成员方法必须要实现)
mixin 多继承,混入类(不能有构造方法)
abstract class 抽象类 (定义抽象方法)
2、类继承, 3个关键字:
extends 继承
implements 继承(父类方法必须要实现)
with 多继承(父类不能有构造方法)
3、约束:
on 约束(继承mixin类的类,必须同时继承约束类)
调用notifyListeners方法通知widget刷新界面
2、 视图部分
接受共享数据
使用ChangeNotifierProvider
Container(
width: double.maxFinite,
child: ChangeNotifierProvider<CountModel>.value(
value: this.countModel,
builder: (context, child) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ChildAPage(),
Text(
context.watch<CountModel>().count.toString()),
],
),
);
},
),
)
NO.1:
ChangeNotifierProvider的 builder方法与child方法区别
ChangeNotifierProvider.value({
Key key,
@required T value,
TransitionBuilder builder,
Widget child,
})
builder: (context, child)
builder的类型是= Widget Function(BuildContext context, Widget child);
这里可以获取当前的context。
@override
Widget buildWithChild(BuildContext context, Widget child) {
assert(
builder != null || child != null,
'$runtimeType used outside of MultiProvider must specify a child',
);
return _InheritedProviderScope<T>(
owner: this,
child: builder != null
? Builder(
builder: (context) => builder(context, child),
)
: child,
);
}
NO.2:
ChangeNotifierProvider<CountModel>.value value方法和 create方法
ChangeNotifierProvider<CountModel>.value(
value: this.countModel
ChangeNotifierProvider<CountModel>(
create: (context) => CountModel(),
ChangeNotifierProvider 非常聪明,它 不会 重复实例化 CartModel,除非在个别场景下。如果该实例已经不会再被调用, ChangeNotifierProvider 也会自动调用 CartModel 的 dispose() 方法。
接受共享数据
调用方法获取model 变化的值。 这两种方法作用相同:
1、context.watch<CountModel>().count
2、Provider.of<CountModel>(context).count
3、 Consumer<CountModel>(
builder: (context, model, child) {
return Text(
'Consumer: ${model.count.toString()}');
},
)
使用 Consumer:
Consumer widget 唯一必须的参数就是 builder。当 ChangeNotifier 发生变化的时候会调用 builder 这个函数。(换言之,当你在模型中调用 notifyListeners() 时,所有相关的 Consumer widget 的 builder 方法都会被调用。)
builder 在被调用的时候会用到三个参数。第一个是 context。在每个 build 方法中都能找到这个参数。
builder 函数的第二个参数是 ChangeNotifier 的实例。它是我们最开始就能得到的实例。你可以通过该实例定义 UI 的内容。
第三个参数是 child,用于优化目的。如果 Consumer 下面有一个庞大的子树,当模型发生改变的时候,该子树 并不会 改变,那么你就可以仅仅创建它一次,然后通过 builder 获得该实例。
3、修改model
在外层父widget可以调用:
countModel.minus();
在子widget可以调用:
Provider.of<CountModel>(context, listen: false).add();
4、 使用MultiProvider
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_test/test/MultiProvider/CountA.dart';
class MultiProviderPage extends StatefulWidget {
const MultiProviderPage({Key key}) : super(key: key);
@override
_MultiProviderState createState() => _MultiProviderState();
}
class _MultiProviderState extends State<MultiProviderPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('MultiProvider使用'),
),
body: SafeArea(
child: Container(
margin: EdgeInsets.only(top: 20, left: 20, right: 20),
child: MultiProvider(
providers: [
//Could not find the correct Provider<CountA> above this MultiProviderPage Widget
ChangeNotifierProvider<CountA>(
create: (_) => CountA(),
),
ChangeNotifierProvider<CountB>(
create: (_) => CountB(),
),
],
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
WidgetA(),
],
),
),
),
));
}
}
class WidgetA extends StatelessWidget {
const WidgetA({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
Provider.of<CountA>(context).astr,
style: TextStyle(color: Colors.black),
),
Text(
context.watch<CountA>().astr,
style: TextStyle(color: Colors.black),
),
Text(
Provider.of<CountB>(context).astr,
style: TextStyle(color: Colors.black),
),
Text(
context.watch<CountB>().astr,
style: TextStyle(color: Colors.black),
),
],
),
ButtonBar(
alignment: MainAxisAlignment.spaceAround,
children: <Widget>[
InkWell(
onTap: () {
DateTime now = new DateTime.now();
Provider.of<CountA>(context, listen: false).astr = 'a:$now';
},
child: Text(
'修改a',
),
),
InkWell(
onTap: () {
DateTime now = new DateTime.now();
Provider.of<CountB>(context, listen: false).astr = 'b:$now';
},
child: Text(
'修改b',
),
),
],
),
],
);
}
}
原理
Provider实际上是个无状态的StatelessWidget,通过包装了InheritedWidget实现父子组件的数据共享,
通过自定义InheritedElement实现刷新。
Provider通过与ChangeNotifier配合使用,实现了观察者模式,Provider会将子组件添加到父组件的依赖关系中,
在notifyListeners()时,会执行InheritedContext.markNeedsNotifyDependents(),将组件标记为dirty等待重绘。
Consumer会只将被它包裹住的子组件注册给父的_dependents形成依赖关系,从而实现了局部更新。
demo
AddPage.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_test/test/ChildPage.dart';
import 'package:provider_test/test/model/count.dart';
class AddPage extends StatefulWidget {
AddPage({this.countModel});
CountModel countModel;
@override
_AddPageState createState() => _AddPageState()..countModel = countModel;
}
class _AddPageState extends State<AddPage> {
CountModel countModel;
@override
void setState(fn) {
// TODO: implement setState
super.setState(fn);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
color: Colors.white,
child: Column(
// mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'我是父widget',
style: TextStyle(
fontSize: 20,
color: Colors.black,
),
),
Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
width: double.maxFinite,
child: ChangeNotifierProvider<CountModel>.value(
value: this.countModel,
builder: (context, child) {
return Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ChildAPage(),
Text(
'countModel: ${countModel.count.toString()}'),
Text(
'Provider.of: ${Provider.of<CountModel>(context).count.toString()}'),
Text(
'context.watch: ${context.watch<CountModel>().count.toString()}'),
Consumer<CountModel>(
builder: (context, model, child) {
return Text(
'Consumer: ${model.count.toString()}');
},
),
],
),
);
},
),
),
Positioned(
bottom: 20,
right: 20,
child: Container(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
FlatButton(
color: Colors.yellow,
onPressed: () {
countModel.add();
},
child: Icon(Icons.add)),
FlatButton(
color: Colors.red,
onPressed: () {
countModel.minus();
},
child: Icon(Icons.remove)),
],
),
),
),
],
),
],
),
),
),
);
}
}
ChildPage.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider_test/test/model/count.dart';
class ChildAPage extends StatelessWidget {
const ChildAPage({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.greenAccent,
child: Column(
children: <Widget>[
Text(
'我是子widget',
style: TextStyle(
fontSize: 20,
),
),
Text('${Provider.of<CountModel>(context).count}'),
Text('${context.watch<CountModel>().count}'),
FlatButton.icon(
color: Colors.red,
icon: Icon(Icons.add_circle_outline),
label: Text('加'),
onPressed: () {
//Provider.of<CountModel>(context).add();
Provider.of<CountModel>(context, listen: false).add();
},
),
FlatButton.icon(
icon: Icon(Icons.remove_circle_outline),
color: Colors.yellow,
label: Text('减'),
onPressed: () {
//Provider.of<CountModel>(context).minus();
Provider.of<CountModel>(context, listen: false).minus();
},
)
],
),
);
}
}
CountModel.dart
import 'package:flutter/material.dart';
class CountModel extends ChangeNotifier {
int _count;
CountModel() : _count = 1;
int get count => _count;
set count(int count) {
_count = count;
notifyListeners();
}
void add() {
this.count = _count+1;
}
void minus() {
this.count = _count-1;
}
}
几种状态同步框架的对比和选择
- [1][Flutter状态管理-Provider的使用和源码解析]https://mp.weixin.qq.com/s/vUhDvHaStrTwbE4tovOqtA