Flutter 中 provider 的使用
Table of Contents
1 Provider 的引入
Provider 解决的是组件之间传递数据的问题
在 Vue 中,父组件通过 props 传递数据给子组件,子组件通过 $emit 通知父组件,然后 Vue 重新渲染
DOM
在 Vue 中,也可以使用 provider 和 inject 传递数据,不过不能很好的处理响应性
与 Vue 相同的是,Flutter 的 Provider 的引入解决了数据传递中嵌套组件过多的问题,若在当前节点没有找到 Provider 组件,
则会通过上下文寻找 Provider ,直至找到
不过想使用 Provider,父组件得暴露出一个对象用以交互, 而这个对象得继承自 ChangeNotifier
以 ChangeNotifierProvider为例
ChangeNotifierProvider( builder: (context) => ExposedObject, child: ... )
子组件想使用其中的值,得通过 Consumer 组件或者 context.read<ExposedType>
Consumer( create: (context, ExposedType expose, child) { // call expose } )
var expose = context.read<ExposedType>();
注意
- 除了 context.read 外,还有 context.watch
- context.watch 会监听 Provider 的变化,调用后会更改页面的渲染,
- context.read 不会监听 Provider 的变化,调用后不会更改页面的渲染,所以不要用在 Widget 的 build 方法中
2 Provider 与 StatefulWidget
Provider 与 StatefulWidget 类似,都是把组件和状态分离开来,
其中 Statefulwidget 使用的是 State 类型来 build 组件,通过 setState 来通知变化
而 Provider 渲染的是其 child 组件,暴露出的组件通过调用 notifyListener 来通知变化,这个时候 Flutter 会丢弃
修改过的组件,直接换新的
3 Counter with Provider
首先是拥有 Provider 的父组件 App
class App extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Counter with Provider', home: Scaffold( appBar: AppBar(title: Text('Counter with Provider'),), body: ChangeNotifierProvider( create: (_) => CounterState(), child: Counter(), ), ) ); } }
接着是暴露出的对象 CounterState
class CounterState extends ChangeNotifier { int count = 0; void increment() { count += 1; notifyListeners(); } }
再是使用 Provider 的子组件 Counter
class Counter extends StatelessWidget { @override Widget build(BuildContext context) { return Column( children: [ Text('You have pushed button many times: '), Text(context.watch<CounterState>().count.toString(), style: TextStyle(fontSize: 16)), FlatButton( child: Text('click me'), onPressed: () { var state = context.read<CounterState>(); state.increment(); }, ), ] ); } }
不过我还是更推荐用 Consumer ,简洁明了
@override Widget build(BuildContext context) { return Consumer( builder: (context, CounterState state, child) { return Column( children: [ Text('You have pushed button many times: '), Text(state.count.toString(), style: TextStyle(fontSize: 16)), FlatButton( child: Text('Click me'), onPressed: () { state.increment(); } ) ] ) } ) }
4 总结
总的来说,使用 Provider 需要注意几点
- 提供暴露的接口
- 状态的更改在接口内部
- 状态的更改需要通知 Provider