为什么使用ChangeNotifier时Stateful Widget会重复重建10次以上?
问题排查结论
重复重建和使用Stateful Widget没有关系,核心原因是Provider的监听范围过大、调用姿势不对导致的不必要重绘:
- 若你在首页
build方法顶层直接调用watch<ThemeNotifier>()或者Provider.of<ThemeNotifier>(context),整个首页组件会被绑定为主题变化的监听者,每次切换主题全页面都会触发重建 - 若页面内多个嵌套子组件也各自监听了ThemeNotifier实例,每次主题变化会触发多层级的重建回调,叠加后就会出现10+次的打印输出
- 若你将整个
MaterialApp都包裹在ThemeNotifier的消费者中,切换主题时整个应用组件树都会重绘,首页作为根路由页面自然会被多次触发重建
优化方案
1. 缩小主题监听范围
只有需要响应主题变化的组件才绑定ThemeNotifier监听,不需要响应主题的部分完全不要读取主题实例:
- 优先用
Selector代替Consumer/watch做局部监听,比如只需要主题亮度时可以写:Selector<ThemeNotifier, Brightness>( selector: (context, notifier) => notifier.currentBrightness, builder: (context, brightness, child) { // 只有brightness变化时这部分才会重建 return Text( "当前主题", style: TextStyle(color: brightness == Brightness.light ? Colors.black : Colors.white), ); }, // 不需要响应主题的子组件放在child参数传入,不会被重复构建 child: const Icon(Icons.sunny), ) - 不要把整个首页都包裹在ThemeNotifier的消费者中,只包裹需要变色的局部组件即可
2. 调整Provider调用姿势
- 仅读取状态、不需要监听变化的场景(比如切换主题的点击回调),调用时添加
listen: false:// 正确,不会绑定监听触发重建 Provider.of<ThemeNotifier>(context, listen: false).toggleTheme(); // 错误,会给当前组件绑定监听,每次主题变化都会触发当前组件重建 Provider.of<ThemeNotifier>(context).toggleTheme(); - 若你的
ChangeNotifierProvider放在MaterialApp上层,只监听theme/darkTheme/themeMode三个属性即可,不要把整个MaterialApp作为消费者的子组件
3. Stateful Widget 适配优化
- 不要在Stateful Widget的
build方法顶层读取ThemeNotifier实例,将读取操作下沉到需要响应主题的子组件中 - 若首页的构造参数没有可变参数,可以给首页加
const构造函数,配合Consumer的child参数可以复用整个首页实例,只有被Consumer/Selector包裹的局部组件会重建
4. 清理多余监听
检查首页和子组件中是否有多处不必要的ThemeNotifier监听,合并重复监听、删除不需要的监听即可减少重复重建的次数
内容的提问来源于stack exchange,提问作者wnjl
相关产品推荐
相关产品推荐

