Flutter 中 provider 的使用
Contents
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 方法中
Provider 与 StatefulWidget
Provider 与 StatefulWidget 类似,都是把组件和状态分离开来, 其中 Statefulwidget 使用的是 State 类型来 build 组件,通过 setState 来通知变化 而 Provider 渲染的是其 child 组件,暴露出的组件通过调用 notifyListener 来通知变化,这个时候 Flutter 会丢弃 修改过的组件,直接换新的
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();
}
)
]
)
}
)
}
总结
总的来说,使用 Provider 需要注意几点
- 提供暴露的接口
- 状态的更改在接口内部
- 状态的更改需要通知 Provider