前言
在Flutter开发中,我们会根据一些状态的值来改变UI样式,setState也是我们常用的状态刷新方式;但是当我们的页面布局比较复杂的时候,我们再用setState的时候,整个页面就会重绘,比较影响APP的性能,这种场景我们就可以只更新局部的组件。
局部更新
我们可以只更新界面的某一个Widget,不需要更新全部的界面,提高加载速度和节约性能的开销,如果是列表,还能够提交流程度,等等。。。
1. setState() 方法
setState是Flutter最简单也是最直接方式了,我们可以把一下复杂的UI拆分封装成不同的Widget,然后在封转的Widget里面进行局部刷新。
示例:
/// 复杂的UI滑竿组件封装成一个单独的Widget
/// 滑竿Widget _customSlider() {double sliderWidth = kXTScreenUtil.screenWidth - 16 * 2;return XTModularNodeSlisder(key: _slisderStateKey,width: sliderWidth,bubbleUnit: "%",isShowBubble: true,alwaysDisplayBubble: true,itemData: _itemData!,onTapEnd: (value, ltvValue){_sliderValueChanged(ltvValue);_updateErrorTips();},);}
2. ValueNotifier 和 ValueListenableBuilder
ValueNotifier 是 Flutter 提供的一种轻量级状态管理工具,可以用于监听特定值的变化并刷新相关 UI。结合 ValueListenableBuilder,可以在特定值改变时触发局部刷新。
示例:
class ZFJWidget extends StatelessWidget {// vnfinal ValueNotifier<int> counter = ValueNotifier<int>(0);@overrideWidget build(BuildContext context) {return Column(children: [ValueListenableBuilder<int>(valueListenable: counter,builder: (context, value, child) {// 仅刷新这个 Text widgetreturn Text('$value');},),ElevatedButton(onPressed: () {// 更新 ValueNotifier,局部刷新counter.value++;},child: Text('Increment'),),],);}
}
3. StreamBuilder
如果需要基于异步数据流进行刷新,StreamBuilder 是一个非常合适的工具。它会监听 Stream,当数据流更新时,局部的 widget 也会自动更新。
当 Stream 中的数据发生变化时,StreamBuilder 会重新构建相关的部分。
class ZFJWidget extends StatelessWidget {// final Stream<int> counterStream = Stream<int>.periodic(Duration(seconds: 1), (x) => x);@overrideWidget build(BuildContext context) {return StreamBuilder<int>(stream: counterStream,builder: (context, snapshot) {if (snapshot.hasData) {return Text('Count: ${snapshot.data}');} else {return CircularProgressIndicator();}},);}
}
4. InheritedWidget
InheritedWidget 是一种提供数据共享的方式,可以在 widget 树中共享状态,并通过该 widget 实现局部刷新。
比如下面的代码,当 Counter 中的数据改变时,它的子 widget 树会触发局部刷新。
示例:
class Counter extends InheritedWidget {final int count;Counter({Key? key, required this.count, required Widget child}) : super(key: key, child: child);static Counter of(BuildContext context) {return context.dependOnInheritedWidgetOfExactType<Counter>()!;}@overridebool updateShouldNotify(Counter oldWidget) {return oldWidget.count != count;}
}class ZFJWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return Counter(count: 10,child: Column(children: [_ChildWidget(),],),);}
}class _ChildWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {final count = Counter.of(context).count;return Text('Count: $count');}
}
5. ChangeNotifier + Consumer(Provider)
通过 ChangeNotifier 和 Provider 实现状态管理与局部刷新。
class Counter extends ChangeNotifier {int _count = 0;int get count => _count;void increment() {_count++;notifyListeners();}
}class CounterProviderWidget extends StatelessWidget {@overrideWidget build(BuildContext context) {return ChangeNotifierProvider(create: (context) => Counter(),child: Column(children: [Consumer<Counter>(builder: (context, counter, child) {return Text('Counter: ${counter.count}');},),ElevatedButton(onPressed: () {Provider.of<Counter>(context, listen: false).increment();},child: Text('Increment'),),],),);}
}
6.RepaintBoundary
通过 RepaintBoundary 控制子 widget 的重绘范围,从而实现性能优化的局部刷新。
class RepaintBoundaryExample extends StatelessWidget {@overrideWidget build(BuildContext context) {return RepaintBoundary(child: Text('This part will be repainted independently'),);}
}
总结
上述几种方式可以根据不同的需求场景来选择。setState 是最基础的局部刷新,ValueNotifier 和 ChangeNotifier 等更适合状态管理场景,而 RepaintBoundary 则更注重性能优化。