一.避免在build方法中进行重复且耗时的工作
因为当父widget重建时,子Widget的build()方法会被频繁地调用。
例如:
- build方法中执行耗时较长的任务。
- 同步文件IO;
- 同步网络IO;
- 计算密集型任务;
- 过深的UI嵌套;
- 复杂的动画效果;
- 等等。
把它们分拆成不同的widget,并进行封装,调用:注意以下规则
慎用页面整体刷新,建议局部刷新,提升绘图性能。
当在State对象上调用setState时,所有子widget都将重建;因此,将setState的调用,转移到实际需要更新的widget子树,使用局部刷新widget来提升性能;避免在widget树的更高层级中,调用setState进行全部更新。
局部刷新有哪些方式呢?
1
2
3
|
setState(() {
// 更新状态
});
|
1
2
3
4
5
6
7
8
9
|
ValueNotifier<int> myValueNotifier = ValueNotifier<int>(0);
ValueListenableBuilder(
valueListenable:myValueNotifier,
builder:(context,value,child) {
return MyWidget(value);
},
);
|
使用方式:使用StreamBuilder监听流的变化,在流中有数据传入时进行局部刷新;可以监听单个流,可以监听多个流;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
Stream<dynamic> combinedStream() {
return Stream<dynamic>.fromFutures([
fetchUserInfo(),
fetchUserOrders(),
fetchUserTicket(),
fetchUserCount(),
fetchUserIsInBlack(),
]);
}
StreamBuilder<dynamic>(
stream: combinedStream(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
debugPrint('data waiting:${snapshot.data}');
return const CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
if (snapshot.hasData) {
debugPrint('Data:${snapshot.data}');
if (snapshot.data is String) {
result += '\n${snapshot.data as String}';
} else if (snapshot.data is num) {
result += '\n${snapshot.data as num}';
} else if (snapshot.data is bool) {
result += '\n 黑名单:${snapshot.data as bool}';
}
}
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('data:$result'),
ElevatedButton(
onPressed: () {
setState(() {});
},
child: const Text('StreamBuilder重新加载~')
),
],
);
}
},
),
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
FutureBuilder(
future: fetchData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
debugPrint('data waiting:${snapshot.data}');
} else if (snapshot.hasError) {
return Text('Error:${snapshot.error}');
} else {
return Text('Data:${snapshot.data}');
}
},
)
|
使用方式:将AnimatedBuilder包裹需要执行动画的widget,在动画值变化时进行局部刷新。
1
2
3
4
5
6
|
AnimatedBuilder(
animation:animationController,
builder:(context,child) {
// 执行动画的UI
},
);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
class SharedDataWidget extends InheritedWidget {
final String data;
const SharedDataWidget(this.data, {super.key, required super.child});
static SharedDataWidget of(BuildContext context) {
// return context.dependOnInheritedWidgetOfExactType<SharedDataWidget>();
final element =
context.getElementForInheritedWidgetOfExactType<SharedDataWidget>()!;
return element.widget as SharedDataWidget;
}
@override
bool updateShouldNotify(SharedDataWidget oldWidget) {
debugPrint('SharedDataWidget data: $data, old data: ${oldWidget.data}');
return data != oldWidget.data;
}
}
class InheritedDemoWidget extends StatelessWidget {
const InheritedDemoWidget({super.key});
@override
Widget build(BuildContext context) {
final sharedDataWidget =
context.dependOnInheritedWidgetOfExactType<SharedDataWidget>();
final data = sharedDataWidget?.data;
debugPrint('InheritedDemoWidget data:$data');
return Text('data:$data');
}
}
|
在 InheritedWidget的子Widget 中,你可以使用 of 方法来获取 InheritedWidget 的状态。当 InheritedWidget 的状态发生变化时,子 Widget 也会重新构建。
- 7.CustomPainter
使用方式:在自定义绘制中,使用CustomPainter 来局部刷新。当绘制参数发生变化时,CustomPainter也会重新构建。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class MyCustomPainter extends CustomPainter {
final double radius;
MyCustomPainter(this.radius);
@override
void paint(Canvas canvas, Size size) {
debugPrint('size:$size, radius:$radius');
final paint = Paint();
paint.color = Colors.green;
paint.style = PaintingStyle.fill;
canvas.drawCircle(Offset(size.width / 2, size.height / 2), radius, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
}
|
使用方式:通过为widget设置key,在需要时重建。
1
2
|
final Key buttonKey = UniqueKey();
ElevatedButton(onPressed: () {}, key: buttonKey, child: const Text('ElevatedButton'),),
|
使用方式:如 riverpod 或 provider,getx,它们提供了更好的状态管理和局部刷新功能,不再赘述。
处理高消耗操作时用 isolates。
列表中的重用。
特别是长列表,注意itemExtent 或 prototypeItem使用。
文章作者
梵梵爸
上次更新
2024-04-06
许可协议
原创文章,如需转载请注明文章作者和出处。谢谢