前言
在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 {
// vn
final ValueNotifier<int> counter = ValueNotifier<int>(0);
@override
Widget build(BuildContext context) {
return Column(
children: [
ValueListenableBuilder<int>(
valueListenable: counter,
builder: (context, value, child) {
// 仅刷新这个 Text widget
return 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);
@override
Widget 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>()!;
}
@override
bool updateShouldNotify(Counter oldWidget) {
return oldWidget.count != count;
}
}
class ZFJWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Counter(
count: 10,
child: Column(
children: [
_ChildWidget(),
],
),
);
}
}
class _ChildWidget extends StatelessWidget {
@override
Widget 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 {
@override
Widget 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 {
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child: Text('This part will be repainted independently'),
);
}
}
总结
上述几种方式可以根据不同的需求场景来选择。setState 是最基础的局部刷新,ValueNotifier 和 ChangeNotifier 等更适合状态管理场景,而 RepaintBoundary 则更注重性能优化。