Flutter中Selector组件调用notifyListeners()时未变更值仍执行builder
问题原因
你代码里的核心问题出在ListenableBuilder的listenable参数使用了context.watch<SomeDataProvider>()。watch方法会让当前的MyHomePage组件订阅SomeDataProvider的变化,一旦调用notifyListeners(),整个MyHomePage的build方法就会被重新执行——这意味着里面所有的Selector、TextButton等子组件都会被重新创建,所以负责展示列表长度的Selector的builder也会被调用,哪怕它监听的beakers根本没变化。
解决方案
把ListenableBuilder里的context.watch改成context.read,因为read只是获取Provider实例,不会让当前组件订阅变化:
ListenableBuilder( listenable: context.read<SomeDataProvider>(), // 替换为read builder: (context, child) { de.log('ListenableBuilder: builder called'); return Text('${context.read<SomeDataProvider>().beakers.length}'); }, ),
额外说明
Selector本身的逻辑是正常的:它会在notifyListeners()触发时,重新执行selector函数获取新值,然后和旧值用==比较,只有值不同时才会调用builder。但如果父组件因为其他原因(比如误用watch)重建,Selector会被重新初始化,这时候builder也会被调用一次——这是组件重建的正常现象,和Selector的监听逻辑无关。
如果需要更精细的重建控制(比如只关心列表长度变化而非List引用),可以给Selector添加shouldRebuild参数自定义判断条件:
Selector<SomeDataProvider, List<Beaker>>( selector: (_,sd)=> sd.beakers, shouldRebuild: (previous, next) => previous.length != next.length, builder: (context, data, child) { de.log('Selector<Listprovider, List<Beaker>>: builder called for change in list'); return Text('${data.length}'); }, ),
内容的提问来源于stack exchange,提问作者UtkarshJ
相关产品推荐
相关产品推荐

