在开发项目的时候,一个比较复杂的页面。如果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为同一个,否则会报错!
不明白的可以交流。