防止Flutter StreamBuilder在ListView中复制数据

时间:2020-01-12 16:59:16

标签: firebase flutter firebase-realtime-database stream-builder

我在StreamBuilder和ListView上遇到问题。

我的初始构建按预期工作,从数据库中加载所有节点并将其添加到ListView:

-节点1 -节点2 -节点3

但是,当将新节点添加到数据库(节点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?中所述)。

任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:1)

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

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