Flutter外层页面加载时子StatefulWidget的initState重复调用问题排查
问题原因
- 你没有给
NotificationListItem指定唯一Key。Flutter的Widget差分匹配规则中,同类型的StatefulWidget如果没有携带Key,父Widget重建时会默认判定子Widget为全新实例,直接销毁旧的State对象,重新调用createState生成新的State实例,initState自然会每次重建时触发。 - 你的
NotificationPage本身是StatelessWidget,每次进入页面时整个页面Widget树都会重新构建,没有做保活处理的情况下,所有子组件都会走完整的创建流程。 - 额外注意:你当前把消息已读状态
_unseenMessage存储在子组件State中本身也存在缺陷,SliverChildBuilderDelegate是懒加载复用机制,列表滚动时滚出可视区域的元素会被回收,再次滚回来时依然会重新创建组件触发initState,已读状态会被重置。
解决方案
方案1(推荐,状态持久化)
将消息已读状态放到上层FCMService的通知数据模型中,和title、body一起存储,子组件直接读取上层状态即可,无需自己维护私有状态。此时NotificationListItem可以直接改为StatelessWidget,从根源避免State重建问题。
方案2(页面内临时保活)
如果仅需要在页面不销毁的前提下避免重建触发initState,给每个NotificationListItem添加唯一的Key即可,示例:
// 假设每条通知有唯一业务id,用ValueKey包裹 child: NotificationListItem( key: ValueKey(Provider.of<FCMService>(context).notificationList[index].id), title: Provider.of<FCMService>(context).notificationList[index].title, content: Provider.of<FCMService>(context).notificationList[index].body, ),
如果你的页面在Tab切换等场景下会被频繁重建,可以将NotificationPage改为StatefulWidget,混入AutomaticKeepAliveClientMixin,重写wantKeepAlive返回true,让页面保持存活不被销毁。
机制纠正
initState只会在同一个State对象首次挂载到Element树时调用一次,只要State对象被销毁(父组件重建匹配失败、列表回收元素、页面销毁都会触发State销毁),下次重新创建组件时都会生成新的State对象,重新执行initState,不是同类型Widget就只会调用一次initState。
内容的提问来源于stack exchange,提问作者Ishan Hettiarachchi
相关产品推荐
相关产品推荐

