在Google IO 2019大会上提出了新的状态管理方案Provider用来替代之前的状态管理方案Provide,针对不同类型对象提供了多种不同的Provider;Provider借助了InheritWidget,将共享状态放到顶层Widget。
很细心的讲解文章
- 借助了InheritWidget,允许将有效信息传递到组件树下的小组件
- 提供DI
- 创建和销毁实例
- 结合Bloc等进行状态管理
Let’s Code
1 2 3 4 5 6
| const Provider.value({ Key key, @required T value, this.updateShouldNotify, this.child }) : dispose = null, super.value(key: key, value: value);
|
上面的源码value的类型为范型,并没有进行限制,所以可以绑定任意数据类型
如何绑定数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class MyApp extends StatelessWidget { @override Widget build (BuildContext context) { return MaterialApp( title: 'demo', home: Provider<String>.value( value: 'demo', child: Demo() ) ) } }
Provider.of()在Provider窗口小部件对应的后代中BuildContext是必需的;获取BuildContext麻烦时Consumer()是很好的替代方式
class MyApp extends StatelessWidget { @override Widget build (BuildContext context) { return MaterialApp( title: 'demo', home: Provider<String>.value( value: 'demo', child: Consumer<String>( builder: (context, value, child) { return Center( child: Text(value) ) } ) ) ) } }
|
如何获取数据
provider需要在绑定的子widget中获取数据,使用静态方法Provider.of(BuildContext context),此方法将从关联的widget树中查找最近的相同类型的数据
1 2 3 4 5 6 7 8 9
| Class Demo extends StatelessWidget { @override Widget build (BuildContext context) { final value = Provider.of<String>(context); return Center( child: Text(value) ) } }
|
#####以上简述了Provider的数据绑定几获取的方法,下面在应用场景里实现Provider的作用,以官方的计数器的例子来举例
首先创建一个Counter类,并封装他的增和减的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Counter with ChangeNotifier{ int _counter; Counter(this._counter); getCouonter => _counter; setCounter(int counter) => _counter = counter; void increment() { _counter ++; notifyListeners(); } void decrement() { _counter --; notifyListeners(); } }
|
现在Counter类拥有了监听的功能,我们需要调用notifyListeners()去通知监听器数据变化,并更新UI
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class MyApp extends StatelessWidget { @override Widget build (BuildContext context) { return MaterialApp( title: 'demo', home: ChangeNotifierProvider<Counter>( builder: (_) => Counter(0), child: HomePage() ) ) } }
Class HomePage extends StatelessWidget { @override Widget build (BuildContext context) { final counter = Provider.of<Counter>(context); return Scaffold( appBar: AppBar( title: Text('ChangeNotifierProvider demo') ), body: Center( child: Text(counter.getCounter()) ), floatingActionButton: Column( children: <Widget>[ FloatingActionButton( onPressed: counter.increment, child: Icon(Icons.add) ), FloatingActionButton( onPressed: counter.decrement, child: Icon(Icons.remove) ), ] ) ) } }
|
Provider结合Bloc模式进行状态管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| class CounterBloc { final _valueController = StreamController<String>(); Stream<String> get stream => _valueController.stream; int _number = 0; void increment() { _number++; _valueController.sink.add(_number.toString()); } void dispose() { _valueController.close(); } }
class ProviderPage extends StatelessWidget { @override Widget build (BuildContext context) { return Provider<CounterBloc>( builder: (_) => CounterBloc(), dispose: (_, bloc) => bloc.dispose(), child: Scaffold( body: CounterText(), floatingActionButton: _floatingButton(), ) ) }
Widget _floatingButton() { return Consumer<CounterBloc>( builder: (context, value, child) { return FloatingActionButton( onPressed: value.increment, child: const Icon(Icons.add), ); } ) } }
class CounterText extends StatelessWidget { @override Widget build (BuildContext context) { final bloc = Provider.of<CounterBloc>(context); return StreamBuilder<String>( stream: bloc.stream, builder: (context, snapshot) { return Center( child: Text(snapshot.data ?? '0') ) } ) } }
|
以上可以用Provder.value()实现,引入简单值传播
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| class ProviderValuePage extends StatefulWidget { @override ProviderValueState createState() => ProviderValueState(); }
class ProviderValueState extends State<ProviderValuePage> { final _bloc = CounterBloc();
@override Widget build(BuildContext context) { return Scaffold( body: Provider<CounterBloc>.value( value: _bloc, child: CounterText(), ), floatingActionButton: FloatingActionButton( onPressed: bloc.increment, child: const Icon(Icons.add), ), ); }
@override void dispose() { _bloc.dispose(); super.dispose(); } }
class CounterText extends StatelessWidget { @override Widget build(BuildContext context) { final bloc = Provider.of<CounterBloc>(context);
return StreamBuilder<String>( stream: bloc.stream, builder: (context, snapshot) { return Center( child: Text(snapshot.data ?? '0') ); }, ); } }
|
ChangeNotifierProvider与ChangeNotifierProvider.value()等提供商的唯一区别在于ChangeNotifierProvider.value()创建和销毁模型实例需要自行处理
多提供商的场景
1 2 3 4 5 6 7 8 9 10
| Provider<Foo>.value( value: foo, child: Provider<Bar>.value( value: bar, child: Provider<Baz>.value( value: baz, child: someWidget ) ) )
|
一层一层的嵌套导致代码可读性降低,所以产生了MultiProvider
1 2 3 4 5 6 7 8
| MultiProvider( providers: [ Provider<Foo>.value(value: foo), Provider<Bar>.value(value: bar), Provider<Baz>.value(value: baz), ], child: someWidget )
|
注意的是需要指定不同的类型,如果是相同的类型,将保留最后一个Provider的值