Firebase 防止颤振StreamBuilder在ListView中复制数据

Firebase 防止颤振StreamBuilder在ListView中复制数据,firebase,flutter,firebase-realtime-database,stream-builder,Firebase,Flutter,Firebase Realtime Database,Stream Builder,我对StreamBuilder和ListView有问题 我的初始构建工作正常,从数据库加载所有节点并将它们添加到ListView: --节点1 --节点2 --节点3 但是,当新节点添加到DB(节点4)时,StreamBuilder会识别更改并将整个节点列表附加到ListView,从而产生重复数据: --节点1 --节点2 --节点3 --节点1 --节点2 --节点3 --节点4 class _HomeScreenState extends State<HomeScreen> {

我对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),
                    );
                  },
                );
            }
          }),
    );
  }
}

类_homescrenstate扩展状态{
数据库引用usersChatsRef=
FirebaseDatabase().reference().child('users-chats');
@凌驾
void initState(){
super.initState();
Stream getData(用户当前用户)异步*{
var userschatstream=usersChatsRef.child(currentUser.uid).onValue;
var foundChats=List();
等待(usersChatsStream中的var userchatsnashot){
Map dictionary=userChatSnapshot.snapshot.value;
如果(字典!=null){
for(dictionary.entries中的变量dictItem){
用户聊天;
如果(dictItem.key!=null){
thisChat=UsersChats.fromMap(dictItem);
}否则{
thisChat=UsersChats();
}
foundChats.add(thisChat);
}
}
产量和产量;
}
}
}
@凌驾
小部件构建(构建上下文){
最终用户=提供者(上下文);
返回脚手架(
appBar:appBar(),
正文:StreamBuilder(
流:getData(用户),
建设者:
(BuildContext上下文,异步快照快照){
if(snap.hasrerror | | |!snap.hasData)
返回文本('Error:${snap.Error}');
开关(快速连接状态){
案例连接状态。正在等待:
返回文本(“加载…”);
违约:
返回ListView.builder(
itemCount:snap.data.length,
itemBuilder:(上下文,索引){
返回列表块(
标题:文本(snap.data[index].id),
字幕:文本(snap.data[index].lastUpdate),
);
},
);
}
}),
);
}
}
classuserschats{
字符串id;
字符串更新;
用户沙茨(
{this.id,
此文件为.lastUpdate});
工厂用户schats.fromMap(映射条目数据){
返回UsersChats(
id:data.key???“”,
lastUpdate:data.value['lastUpdate']??'';
}
}
我在build方法之外引用流,因为我需要在流上执行多个异步函数(如本线程中所讨论的)


任何帮助都将不胜感激

发布工作代码。再次感谢雷米的提示!!希望这段代码对其他人有帮助

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);
            },
          );
        },
      ),
    );
  }
}


class\u homescrenstate扩展状态{
数据库引用usersChatsRef=
FirebaseDatabase().reference().child('users-chats');
列出mychat=[];
StreamSubscription\u StreamSubscription;
@凌驾
void initState(){
super.initState();
_streamSubscription=getData()。侦听((数据){
设置状态(){
myChats=数据;
});
});
}
@凌驾
无效处置(){
_streamSubscription?.cancel();//不要忘记关闭订阅
super.dispose();
}
流getData()异步*{
var userschatstream=usersChatsRef.child('userId').onValue;
var foundChats=List();
等待(usersChatsStream中的var userchatsnashot){
foundChats.clear();
Map dictionary=userChatSnapshot.snapshot.value;
如果(字典!=null){
for(dictionary.entries中的变量dictItem){
用户聊天;
如果(dictItem.key!=null){
thisChat=UsersChats.fromMap(dictItem);
}否则{
thisChat=UsersChats();
}
foundChats.add(thisChat);
}
}
产量和产量;
}
}
@凌驾
小部件构建(构建上下文){
最终用户=提供者(上下文);
返回脚手架(
appBar:appBar(),
正文:ListView.builder(
itemCount:myChats.length,
itemBuilder:(上下文,索引){
标题:文本(myChats[index].id),
字幕:文本(myChats[index].lastUpdate),
onTap:(){
Navigator.pushNamed(上下文,MessagesScreen.id);
},
);
},
),
);
}
}

听起来像是
usersChatsRef.child(currentUser.uid)。onValue
会在每次更改时发出整个列表,而不是只推送差异。这意味着您不必“添加”到
foundChat
,只需删除之前的列表即可。感谢您的快速回复!您知道如何修改
getData
函数吗?一直在尝试不同的方法,但到目前为止没有任何运气。请尝试清除“等待”的第一行中的
foundChats
!!做到了,非常感谢!!!剩下的唯一问题是,当我使用
Navigator.push
并导航到主屏幕,ListView的数据就会消失。可能需要StreamSubscription(如您在此处概述的:)?我正在努力添加这个,如果成功,我会发布我的结果。
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);
            },
          );
        },
      ),
    );
  }
}