防止 Flutter StreamBuilder 在 ListView 中复制数据
Posted
技术标签:
【中文标题】防止 Flutter StreamBuilder 在 ListView 中复制数据【英文标题】:Preventing Flutter StreamBuilder from Duplicating Data in ListView 【发布时间】:2020-04-29 13:06:39 【问题描述】:我遇到了 StreamBuilder 和 ListView 的问题。
我的初始构建按预期工作,从我的数据库加载所有节点并将它们添加到 ListView:
--节点1 --节点2 --节点3
但是,当向 DB(节点 4)添加新节点时,StreamBuilder 会识别更改并将整个节点列表附加到 ListView,从而导致重复数据:
--节点1 --节点2 --节点 3 --节点1 --节点2 --节点 3 --节点4
class _HomeScreenState extends State<HomeScreen>
DatabaseReference usersChatsRef =
FirebaseDatabase().reference().child('users-chats');
@override
void initState()
super.initState();
Stream<List<UsersChats>> getData(User currentUser) async*
var usersChatsStream = usersChatsRef.child(currentUser.uid).onValue;
var foundChats = List<UsersChats>();
await for (var userChatSnapshot in usersChatsStream)
Map dictionary = userChatSnapshot.snapshot.value;
if (dictionary != null)
for (var dictItem in dictionary.entries)
UsersChats thisChat;
if (dictItem.key != null)
thisChat = UsersChats.fromMap(dictItem);
else
thisChat = UsersChats();
foundChats.add(thisChat);
yield foundChats;
@override
Widget build(BuildContext context)
final user = Provider.of<User>(context);
return Scaffold(
appBar: AppBar(),
body: StreamBuilder<List<UsersChats>>(
stream: getData(user),
builder:
(BuildContext context, AsyncSnapshot<List<UsersChats>> snap)
if (snap.hasError || !snap.hasData)
return Text('Error: $snap.error');
switch (snap.connectionState)
case ConnectionState.waiting:
return Text("Loading...");
default:
return ListView.builder(
itemCount: snap.data.length,
itemBuilder: (context, index)
return ListTile(
title: Text(snap.data[index].id),
subtitle: Text(snap.data[index].lastUpdate),
);
,
);
),
);
class UsersChats
String id;
String lastUpdate;
UsersChats(
this.id,
this.lastUpdate);
factory UsersChats.fromMap(MapEntry<dynamic, dynamic> data)
return UsersChats(
id: data.key ?? '',
lastUpdate: data.value['lastUpdate'] ?? '');
我在 build 方法之外引用流,因为我需要在流上执行多个异步函数(如本线程 How do I join data from two Firestore collections in Flutter? 中所述)。
任何帮助将不胜感激!
【问题讨论】:
听起来usersChatsRef.child(currentUser.uid).onValue
在每次更改时都会发出整个列表,而不是只推送差异。这意味着您不必“添加”到foundChat
,只需删除之前的列表即可。
感谢您的快速回复!你知道我会如何修改getData
函数吗?一直在尝试不同的方法,但到目前为止没有任何运气。
尝试在“等待”的第一行清除foundChats
太棒了!做到了,非常感谢!!!
剩下的唯一问题是,当我使用Navigator.push
并导航离开然后返回主屏幕时,ListView 的数据消失了。也许需要 StreamSubscription(正如您在此处概述的那样:***.com/questions/54101589/…)?我正在努力添加这个,如果成功我会发布我的结果。
【参考方案1】:
发布工作代码。再次感谢雷米的提示!希望此代码对其他人有所帮助!
class _HomeScreenState extends State<HomeScreen>
DatabaseReference usersChatsRef =
FirebaseDatabase().reference().child('users-chats');
List<UsersChats> myChats = [];
StreamSubscription _streamSubscription;
@override
void initState()
super.initState();
_streamSubscription = getData().listen((data)
setState(()
myChats = data;
);
);
@override
void dispose()
_streamSubscription?.cancel(); // don't forget to close subscription
super.dispose();
Stream<List<UsersChats>> getData() async*
var usersChatsStream = usersChatsRef.child('userId').onValue;
var foundChats = List<UsersChats>();
await for (var userChatSnapshot in usersChatsStream)
foundChats.clear();
Map dictionary = userChatSnapshot.snapshot.value;
if (dictionary != null)
for (var dictItem in dictionary.entries)
UsersChats thisChat;
if (dictItem.key != null)
thisChat = UsersChats.fromMap(dictItem);
else
thisChat = UsersChats();
foundChats.add(thisChat);
yield foundChats;
@override
Widget build(BuildContext context)
final user = Provider.of<User>(context);
return Scaffold(
appBar: AppBar(),
body: ListView.builder(
itemCount: myChats.length,
itemBuilder: (context, index)
title: Text(myChats[index].id),
subtitle: Text(myChats[index].lastUpdate),
onTap: ()
Navigator.pushNamed(context, MessagesScreen.id);
,
);
,
),
);
【讨论】:
以上是关于防止 Flutter StreamBuilder 在 ListView 中复制数据的主要内容,如果未能解决你的问题,请参考以下文章
来自一个 StreamBuilder 的 Flutter 快照显示在另一个 StreamBuilder 中