如何避免Flutter中StreamBuilder被不必要多次执行的问题
问题根因
你当前遇到的底部导航切换后重复触发加载的核心原因是:普通StatefulWidget在被切出导航可见区域时,默认会销毁对应的State实例,切回时会重新初始化State、执行build逻辑。哪怕你在initState中提前初始化了Stream,每次切回都会生成新的Stream实例重新订阅,因此会先触发加载状态。
解决方案1(无状态管理依赖,快速生效)
给当前页面的State混入AutomaticKeepAliveClientMixin,让页面被切走时保持State不被销毁,缓存已有状态和Stream订阅:
class _DeparmentsState extends State<Deparments> with AutomaticKeepAliveClientMixin { // 新增混入 late final Stream<QuerySnapshot<Object?>> _widget; Stream<QuerySnapshot<Object?>> getProds(){ CollectionReference ref = FirebaseFirestore.instance.collection("Departamentos"); return ref.snapshots(); } // 重写wantKeepAlive返回true,开启状态保持 @override bool get wantKeepAlive => true; @override void initState() { super.initState(); _widget = getProds(); } @override Widget build(BuildContext context) { super.build(context); // 必须调用父类方法 // 其余原有build逻辑不变 return Scaffold( drawer: SideMenu(auth: widget.auth), appBar: AppBar( title: Text("Departamentos"), centerTitle: true, backgroundColor: Colors.green, ), body: Container( child: StreamBuilder<QuerySnapshot> ( stream: _widget, builder: (BuildContext context, snapshot) { if (!snapshot.hasData) { return Center( child: CircularProgressIndicator(), ); } else { List deparments = snapshot.data!.docs.map((doc) => doc.id).toList(); return Column( children: [ Expanded( child: ListView.builder( padding: EdgeInsets.only(top: 10), scrollDirection: Axis.vertical, itemCount: deparments.length, itemBuilder: (context, index) { return SingleChildScrollView( child: Card( child: Text(deparments[index]), ), ); }), ) ], ); } }), ), ); } }
解决方案2(符合最佳实践,适配多页面共享数据场景)
使用状态管理框架(Provider、Bloc、GetX等)将Firestore的Stream订阅逻辑抽离到独立的状态层,全局缓存已拉取的列表数据。页面重建时直接读取状态层的缓存数据,不需要重新发起订阅请求,既可以解决当前重复加载的问题,也方便其他页面共享部门列表数据。
内容的提问来源于stack exchange,提问作者Alexis Chavarría
相关产品推荐
相关产品推荐

