Flutter中Firebase数据监听器调用后返回空列表怎么办
问题根因
拿到空列表是异步时序逻辑错误导致的:
activateChatListener方法中,注册完onValue监听后会立刻执行return dataList,此时Firebase还没返回任何数据,dataList本身就是空的。onValue传入的回调是异步延迟执行的:只有数据库完成首次数据推送,回调里往dataList添加数据的逻辑才会运行。但这时候你已经把空列表返回给组件、完成了setState,后续回调修改dataList不会触发UI重绘,所以打印结果永远为空。
修复方案
方案1:等待首次数据返回再回传结果
如果只需要首次加载的聊天数据,可以用Completer等第一次数据推送完成后再返回结果,修改message_dao代码如下:
Future<List> activateChatListener() async { final completer = Completer<List>(); List dataList = []; _receivedChatsStream = _messagesRef.onValue.listen((event) { // 每次触发先清空旧数据,避免重复累加 dataList.clear(); event.snapshot.children.forEach((snapshot) { dataList.add(snapshot.value); }); // 首次数据返回时完成异步回调 if (!completer.isCompleted) { completer.complete(dataList); } }); return completer.future; }
注意:这个方案只能拿到首次加载的数据,后续数据库有新消息推送时,
dataList虽然会在内存中更新,但不会自动触发UI刷新,要做实时消息更新更推荐用流方案。
方案2:暴露数据流用StreamBuilder响应更新
这是更符合Flutter响应式设计的写法,不需要在initState里手动调加载方法,新消息到达时会自动刷新UI:
首先修改message_dao,直接输出处理好的数据流:
Stream<List> get chatListStream { return _messagesRef.onValue.map((event) { List dataList = []; event.snapshot.children.forEach((snapshot) { dataList.add(snapshot.value); }); return dataList; }); }
然后在receivedChats的build方法里直接用StreamBuilder渲染列表:
@override Widget build(BuildContext context) { return StreamBuilder<List>( stream: messageDao.chatListStream, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return Center(child: Text('聊天记录加载失败')); } final chats = snapshot.data ?? []; return ListView.builder( itemCount: chats.length, itemBuilder: (context, index) => ListTile( title: Text(chats[index].toString()), ), ); }, ); }
额外注意点
- 原逻辑每次触发
onValue都会往dataList里追加数据,多次触发会出现重复条目,需要在每次处理事件前先清空列表旧数据。 - 页面销毁时必须调用
_receivedChatsStream.cancel()取消监听,否则会造成内存泄漏。
内容的提问来源于stack exchange,提问作者whatwhatwhat
相关产品推荐
相关产品推荐

