Flutter Web调整浏览器窗口时Stream持续卡在Waiting状态
问题产生原因
核心问题是Stream实例的创建位置错误,直接写在了build方法中:
- Flutter Web调整浏览器窗口尺寸时,窗口宽高变化会触发依赖
MediaQuery的所有上层节点重建,整棵子树的build方法会被反复执行 - 每次重建时,
ContactDataService.getContactById(id)都会返回一个全新的Stream实例,而非之前已经完成数据加载的旧流 StreamBuilder接收到新传入的Stream时,会自动重置连接状态,回到ConnectionState.waiting初始状态。加上Firebase Web SDK新建流监听时存在注册延迟,就会出现一直卡在加载组件、拿不到数据的问题- 多数开发者实现Firestore流查询时,每次调用
snapshots()都会生成新的流订阅,没有做实例缓存,会进一步放大这个问题
修复方案
按照以下步骤调整即可解决问题:
- 将Stream实例的创建逻辑从
build方法迁移到State生命周期中,仅初始化一次,后续build直接复用已有实例,避免每次重建都生成新流。
参考实现代码:class _ContactDetailPageState extends State<ContactDetailPage> { late Stream<Contact?> _contactStream; @override void initState() { super.initState(); // 初始化阶段只创建一次流 _contactStream = ContactDataService.getContactById(widget.id); } @override void didUpdateWidget(covariant ContactDetailPage oldWidget) { super.didUpdateWidget(oldWidget); // 当传入的联系人id变化时,再更新流实例 if (oldWidget.id != widget.id) { _contactStream = ContactDataService.getContactById(widget.id); } } @override Widget build(BuildContext context) { return CustomScaffold( body: StreamBuilder<Contact?>( // 直接复用提前初始化的流实例,禁止在build中新建流 stream: _contactStream, builder: (context, contactSnapshot) { if (contactSnapshot.connectionState == ConnectionState.waiting) { return LoadingWidget(); } if (contactSnapshot.hasError || contactSnapshot.data == null) { return Center( child: SizedBox( width: 600, child: ErrorCard( message: 'Error: ${contactSnapshot.error ?? "未查询到对应联系人数据"}'), )); } Contact contact = contactSnapshot.data!; return Column( children: [ Expanded( child: CustomSubScaffoldWrapper( appActions: _contactActions(contact, context), subScaffoldPages: _contactPages(contact, contactScaffoldKey), intialPage: widget.selectedPage, subScaffoldKey: contactScaffoldKey, )) ], ); })); } } - 在数据服务层增加流缓存逻辑,避免相同参数的请求反复创建新的Firestore流订阅,减少重复的监听注册开销。
参考实现:class ContactDataService { // 缓存不同id对应的流实例 static final Map<String, Stream<Contact?>> _contactStreamCache = {}; static Stream<Contact?> getContactById(String id) { // 已有缓存直接返回 if (_contactStreamCache.containsKey(id)) { return _contactStreamCache[id]!; } // 原有Firestore查询逻辑 final newStream = FirebaseFirestore.instance .collection('contacts') .doc(id) .withConverter<Contact>( fromFirestore: (snapshot, _) => Contact.fromJson(snapshot.data()!, snapshot.id), toFirestore: (contact, _) => contact.toJson(), ) .snapshots() .map((docSnapshot) => docSnapshot.data()); _contactStreamCache[id] = newStream; return newStream; } } - 体验优化(非必须):可以给
StreamBuilder传入initialData参数,使用本地缓存的联系人数据作为初始值,避免极端场景下的加载闪动。
内容的提问来源于stack exchange,提问作者Freddy Straub
相关产品推荐
相关产品推荐

