Предотвращение дублирования данных в Flutter StreamBuilder в ListView - PullRequest
0 голосов
/ 12 января 2020

У меня проблема с StreamBuilder и ListView.

Моя первоначальная сборка работает как задумано, загружая все узлы из моей БД и добавляя их в ListView:

- Узел 1 - Узел 2 - Узел 3

Однако когда новый узел добавляется в БД (Узел 4), StreamBuilder распознает изменение и добавляет весь список узлов в ListView, что приводит к дублированию данных:

- Узел 1 - Узел 2 --Node 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'] ?? '');
  }
}

Я ссылаюсь на поток вне метода сборки, потому что Мне нужно выполнить несколько асин c функций в потоке (как обсуждено в этой теме Как мне объединить данные из двух коллекций Firestore во Flutter? ).

Любая помощь будет в значительной степени оценили!

1 Ответ

1 голос
/ 12 января 2020

Размещение рабочего кода. Еще раз спасибо Реми за советы! Надеюсь, этот код поможет кому-то еще!

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


...