Flutter中Provider传模型更新时Stateless子组件不重建问题
问题现象
当ItemTile的父组件传入更新后的item属性触发ItemTile重建时,作为子组件的ItemTileTitle不会同步重建,即使ItemTileTitle内部通过context.select监听了ItemProvider中的item值,也没有触发预期的更新。
核心代码如下:
class ItemTile extends StatelessWidget { const ItemTile({ Key? key, required this.item, }) : super(key: key); final ItemModel item; @override Widget build(BuildContext context) { return Provider( create: (context) => ItemProvider(item: item), child: ListTile( onTap: () => Navigator.push( context, ItemDetailsPage(onNotification: MyPageRoute.onNotification(context), item: item), ), title: const ItemTileTitle(), ), ); } } class ItemTileTitle extends StatelessWidget { const ItemTileTitle({ Key? key, }) : super(key: key); @override Widget build(BuildContext context) { final item = context.select<ItemProvider, ItemModel>((provider) => provider.item); return Text('ID: ${item.id}, Status: ${item.status}'); } }
产生原因
问题出在Provider的生命周期和context.select的监听逻辑上:
Provider的create回调仅在组件首次插入组件树时执行一次,用来创建并持有ItemProvider实例。后续父组件重建导致Provider组件重新构建时,不会再次执行create回调,旧的ItemProvider实例会被继续复用。父组件传入的新item值,根本没有被同步到已经存在的ItemProvider实例中,实例内部持有的还是第一次初始化时传入的旧item。context.select的触发逻辑是:仅当它监听的ItemProvider实例中,被选中的item值发生变化时,才会通知组件重建。既然ItemProvider实例内部的item从来没被更新过,自然不会触发变更通知,ItemTileTitle也就不会重建。- 之前的推测存在误区:不是组件树结构未发生变化导致子组件不重建,本质是Provider持有的状态对象从未收到新的入参,select根本监听不到任何值变化。
修复方案
- 给
Provider补充update回调,当父组件传入的参数变化时,同步更新已复用的ItemProvider实例内部的值,示例代码如下:
Provider<ItemProvider>( create: (context) => ItemProvider(item: item), update: (context, previousProvider) { // 父组件更新item时,同步修改已有provider实例的内部值 previousProvider.item = item; return previousProvider; }, child: ListTile( // 原有ListTile逻辑保持不变 ), )
- 如果
ItemProvider是继承自ChangeNotifier的可监听对象,更新值后记得调用notifyListeners(),或者直接使用ProxyProvider/ChangeNotifierProxyProvider来实现外部参数到内部状态的同步逻辑,避免手动处理实例更新。
内容的提问来源于stack exchange,提问作者HeyShafty
相关产品推荐
相关产品推荐

