在开发项目的时候,一个比较复杂的页面。如果setstate会触发build导致整个页面刷新。页面会闪烁。
有时候,往往需要刷新的只是其中一个很小的widget. 这个时候我们就需要局部刷新。
原理就是将需要刷新的widget单独提取为一个statefulwidget,然后再其内部调用setState方法来刷新。
可以用的框架有
Provider
Redux
或者直接StreamController + StreamBuilder
我使用过Provider与StreamBuilder的两种方法。
先说说Provider的方式:
首先创建Model
class FilterModel with ChangeNotifier {
int _count = 0;
bool _isOpen = false;
int get value => _count;
bool get isOpen => _isOpen;
void increment() {
_count++;
notifyListeners();
}
void switchAction() {
_isOpen = !_isOpen;
notifiListeners();
}
}
然后在App入口处使用Provider
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CounterProvider()),,
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(),
initialRoute: '/login',
onGenerateRoute: Router.generateRoute,
),
);
}
}
然后在子页面中就可以引用Provider了。
引用的方式有:
Consumer, 监听某个Provider
Selector, 监听某个Provider中的某一个
Provider.of, 监听或刷新
context.watch(), 一方法使得widget能够监听泛型T上发生的改变。
context.read(),直接返回T,不会监听改变。
context.select<T, R>(R cb(T value)),允许widget只监听T上的一部分®。
Consumer的方式就不说了, 只要Provider中有一个值改变,就会触发刷新。
Selector指定刷新区域,原理是缓存了Widget, 如果值改变了就调用builder刷新, 否则使用cache的Widget
Selector<FilterModel, bool>(
selector: (context, state) => state.isOpen,
builder: (ctx, data, child) {
return Switch(
value: data,
activeColor: Color(0xFF9168DA),
onChanged: (bool val) {
filterModel.switchAction();
},
);
},
)
下面通过Builder是可以获取需要的值,就不需要再最外面声明很多变量,再需要的时候再声明,这是一个技巧。
Container(
child: Builder(builder: (context) {
final isOpen= context.select((FilterModel state) => state.isOpen);
return Switch(
value: isOpen,
activeColor: Color(0xFF9168DA),
onChanged: (val) {
filterModel.switchAction();
},
);
}),
)
注意,Provider是不能跨router使用的哦。 比如你在A页面引入Provider, 在B页面去获取,会报错哦。
在同一个页面,Provider.of(context), 这个context必须是子context, 不能与page的context为同一个,否则会报错!
不明白的可以交流。