Flutter StreamBuilder渲染Firebase流数据时出现重复问题
问题根因
- Firestore的
snapshots()方法返回的是持续推送数据变更的实时流,并非一次性拉取完成的请求,永远不会触发ConnectionState.done状态,你代码中写在done分支里的逻辑本身就不会执行。 - 每次StreamBuilder触发重建(页面重开、流推送新数据、组件树重绘都会触发),你都在向全局
_contexts列表执行add追加操作,从未基于最新快照全量替换列表数据,重复打开页面时数据自然会反复累加。之前尝试的清空列表方案未生效,本质是清空时机错误,没有在拿到最新快照的第一时间先清空再赋值。 - 你使用全局变量在组件间传递数据,
TestWidgetContext的选项列表是在initState中从全局变量初始化生成的,后续全局列表更新时,子组件的State不会同步刷新,会长期持有旧数据。
修复方案
1. 修正StreamBuilder逻辑
不要在builder中累加全局列表数据,每次拿到新快照直接基于快照生成当前全量数据,通过构造函数传递给子组件,参考代码如下:
Flexible( child: StreamBuilder<QuerySnapshot>( stream: FirebaseFirestore.instance .collection('Users') .doc(FirebaseAuth.instance.currentUser?.uid) .collection('contexts') .snapshots(), builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { // 错误状态处理 if (snapshot.hasError) { return const Text('加载失败'); } // 加载等待状态 if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 直接从当前快照全量生成上下文列表,不要做add累加 List<String> contexts = snapshot.data!.docs.map((doc) { return doc['context_Name'] as String; }).toList(); // 将列表通过构造函数传给子组件,不依赖全局变量 return TestWidgetContext(contexts: contexts); }, ), )
2. 改造子组件接收入参
移除子组件对全局变量的依赖,通过构造函数接收父组件传入的最新列表,每次组件重建时都生成最新的选项配置:
class TestWidgetContext extends StatefulWidget { final List<String> contexts; const TestWidgetContext({Key? key, required this.contexts}) : super(key: key); @override State<TestWidgetContext> createState() => _TestWidgetContextState(); } class _TestWidgetContextState extends State<TestWidgetContext> { List<String>? _selectedContext; @override Widget build(BuildContext context) { // 每次build都基于父组件传入的最新值生成选项 final itemsContext = widget.contexts .map((item) => MultiSelectItem(item, item)) .toList(); return Column( children: [ MultiSelectBottomSheetField( buttonText: const Text("Contexts"), onConfirm: (val) { setState(() { _selectedContext = List<String>.from(val); }); }, items: itemsContext, initialValue: _selectedContext ?? [], ), ], ); } }
注意事项
- 如果你不需要实时监听数据变更,只是进入页面时拉取一次数据,不要用
snapshots()流,改用get()方法获取Future,配合FutureBuilder渲染即可,从根源上避免流多次触发重建的问题。 - 不要在StreamBuilder、FutureBuilder的builder回调中执行全局变量写入、数据累加这类副作用操作,builder方法在组件生命周期中会被反复触发,这类逻辑会重复执行产生异常。
- 组件间传值优先使用构造函数参数传递,尽量不要依赖全局变量,避免出现状态不同步、数据污染的问题。
内容的提问来源于stack exchange,提问作者Laurent Thomas
相关产品推荐
相关产品推荐

