不得不说 Flutter 还很年轻,Flutter 和其生态环境都在告诉迭代,很多公司和开发者现在开始了 Flutter 的尝试。比如 Flutter 1.12+ ,比如 Provider 也迭代到了 Provider4,有一些变更,最重要的是增加了 Selector 实现了更细粒度的控制,使用过后就有一种想替换掉所有 Consumer 。
如果没有了解过 Provider 的同志,可以看看我很久很久之前写了两篇关于 Provider3 的文章,Flutter开始干系列-状态管理Provider3 简单介绍了Provider, Flutter开始干系列-完整列表与Provider3实战展现了一个简单的实战效果。
话不多说先上2张gif效果:
Consumer

Selector

细心的同学可能已经看出来区别了。使用 Consumer 效果中点击 Increment 时,除了顶部的较大的数字变动外,下面的 Increment build 后面的数字也跟着变动了。使用 Selector 效果中点击 Increment 时,只有顶部的较大的数字变动。
实现 ChangeNotifer
首先实现一个 CounterNotifier 实现计数并在数字并通知更新,一般情况下我们使用 Provider 都是需要和 ChangeNotifer 一起使用。
class CounterNotifier with ChangeNotifier {
int _count = 0;
int _count1 = 0;
int _buildCount = 0;
int get count => _count;
int get count1 => _count1;
int get buildCount => _buildCount;
increment() {
_count++;
notifyListeners();
}
increment1() {
_count1++;
notifyListeners();
}
}
使用 Consumer
class _ProvidePageState extends State<ProvidePage> {
CounterNotifier _counter = CounterNotifier();
@override
Widget build(BuildContext context) {
print('build');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ChangeNotifierProvider.value(
value: _counter,
child: Column(
children: <Widget>[
Consumer<CounterNotifier>(
builder: (context, v, child) {
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Increment1 build ${_counter.count1} count => You have pushed the button this many times:',
),
Text(
'${_counter.count}',
style: Theme.of(context).textTheme.display1,
),
FlatButton(
onPressed: () {
_counter.increment();
},
child: Text('Increment'))
],
),
);
},
),
Consumer<CounterNotifier>(
builder: (context, v, child) {
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Increment build ${_counter.count} count => You have pushed the button this many times:',
),
Text(
'${_counter.count1}',
style: Theme.of(context).textTheme.display1,
),
FlatButton(
onPressed: () {
_counter.increment1();
},
child: Text('Increment1'))
],
),
);
},
),
],
),
),
),
);
}
}
Demo 不复杂,这里 ChangeNotifierProvider 持有了 CounterNotifier ,这里使用了两个 Consumer 实现了局部刷新。通过上面的效果图可以看到,一个 Consumer 中的按钮点击,除了刷新了当前的 Consumer,另一个 Consumer 也刷新重建了。
使用 Selector
class _ProvidePageState extends State<ProvidePage> {
CounterNotifier _counter = CounterNotifier();
@override
Widget build(BuildContext context) {
print('build');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: ChangeNotifierProvider.value(
value: _counter,
child: Column(
children: <Widget>[
Selector<CounterNotifier, int>(
selector: (_, notifier) => notifier.count,
// shouldRebuild:(previous,next)=> false,
builder: (context, value, child) {
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Increment1 build ${_counter.count1} count => You have pushed the button this many times:',
),
Text(
'${_counter.count}',
style: Theme.of(context).textTheme.display1,
),
FlatButton(
onPressed: () {
_counter.increment();
},
child: Text('Increment'))
],
),
);
},
),
Selector<CounterNotifier, int>(
selector: (_, notifier) => notifier.count1,
builder: (context, value, child) {
return Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Increment build ${_counter.count} count => You have pushed the button this many times:',
),
Text(
'${_counter.count1}',
style: Theme.of(context).textTheme.display1,
),
FlatButton(
onPressed: () {
_counter.increment1();
},
child: Text('Increment1'))
],
),
);
},
),
],
),
),
),
);
}
}
Demo 也不复杂,主要的区别仅仅是将 Consumer 换成了 Selector 其他都一样。但是这时候点击哪个 Selector 就只有哪个 Selector 重建刷新了。
Seletor 介绍
上面我们直接就上了效果图和代码,最后简单介绍下 Selector。
Selector({
Key key,
// Widget Function(BuildContext context, T value, Widget child)
// 用于构建 Widget
@required ValueWidgetBuilder<S> builder,
// S Function(BuildContext, A)
// 用于指定使用哪个值作为重重建判断依据
@required S Function(BuildContext, A) selector,
// bool Function(T previous, T next)
// 是否重建,一般情况下不用我们实现
ShouldRebuild<S> shouldRebuild,
Widget child,
})
Selector 有 Selector0 ~ Selector6,具体使用大同小异,可以传 0 ~ 6 个 ChangeNotifer,但是 S(Value)泛型只有一个,如果需要多个个值同时判断返回 S 怎么办呢?这个时候可以使用元祖 Tuple 。
最后附上Demo地址(详见main3、main4):github.com/joker-fu/fl…