title: Flutter chat app on Firebase, StreamBuilder reloads blinking and calls on null value when ListView is created
body: <p>I'm making a FreeLancer app with a chat function. First, I want the chat list that renders chat tiles from Firebase so I use GetX Stream (is this like streamBuilder?) to render the chat tile representing, and in that chat tile (I'm using ListTile) the subtitle text is pulled from FireStore from the latest message using StreamBuilder</p>
  <p><strong>Chat List Screen</strong></p>
  <pre class="lang-dart prettyprint-override"><code>final user = FirebaseAuth.instance.currentUser;
      return user == null
          ? const Center(
              child: Text('Please login to use Chat'),
            )
          : Scaffold(
              floatingActionButton: FloatingActionButton(
                onPressed: () async {
                  ChatService().addRoom(randomString());
                },
                child: const Icon(FontAwesomeIcons.solidMessage),
              ),
              appBar: AppBar(
                title: const Text('Chats'),
                centerTitle: true,
              ),
              body: GetX&lt;ChatController&gt;(
                init: ChatController(),
                builder: (roomList) {
                  return ListView.builder(
                    itemCount: roomList.rooms.length,
                    itemBuilder: (context, index) {
                      return ChatTile(roomList.rooms[index]);
                    },
                  );
                },
              ),
  </code></pre>
  <p><strong>Chat Tile</strong></p>
  <pre class="lang-dart prettyprint-override"><code>final db = FirebaseFirestore.instance.collection('Rooms').doc(room.roomId);
      final stream = db.snapshots();
      var roomName = 'Loading...';
      return StreamBuilder(
        stream: stream,
        builder: (context, snapshot) {
          final roomInfo = snapshot.data as dynamic;
          return Column(
            children: [
              InkWell(
                onTap: () {},
                child: SizedBox(
                  height: MediaQuery.of(context).size.height * 0.08,
                  child: Center(
                    child: ListTile(
                      contentPadding: const EdgeInsets.symmetric(horizontal: 10),
                      leading: const FlutterLogo(size: 50),
                      title: Text(roomInfo['roomName']),
                      subtitle: LastMessage(room.roomId),
                      trailing: Text(timeago.format(
                          (roomInfo['lastestMsg'] as Timestamp).toDate())),
                    ),
                  ),
                ),
              ),
              const Divider()
            ],
          );
        },
      );
  </code></pre>
  <p><strong>Latest Message</strong></p>
  <pre class="lang-dart prettyprint-override"><code>final db = FirebaseFirestore.instance.collection('Rooms').doc(roomId);
      final stream = db
          .collection('message')
          .orderBy('createdDate', descending: true)
          .snapshots();
      return StreamBuilder&lt;QuerySnapshot&gt;(
          stream: stream,
          builder: (context, snapshot) {
            final messages = snapshot.data!.docs;
            final message = messages.first.data() as dynamic;
            return Text(
              message['content'],
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
            );
          });
  </code></pre>
  <p><strong>Chat Room Model</strong></p>
  <pre class="lang-dart prettyprint-override"><code>String roomId;
    String roomName;
    DateTime createDate;
    bool isDeleted;
    DateTime lastestMsg;
    List&lt;String&gt; members;
    ChatRoom({
      required this.roomId,
      required this.roomName,
      required this.createDate,
      required this.isDeleted,
      required this.lastestMsg,
      required this.members,
    });
  factory ChatRoom.fromMap(Map&lt;String, dynamic&gt; map) {
      return ChatRoom(
        roomId: map['roomId'] as String,
        roomName: map['roomName'] as String,
        createDate: (map['createDate'] as Timestamp).toDate(),
        isDeleted: map['isDeleted'] as bool,
        lastestMsg: (map['lastestMsg'] as Timestamp).toDate(),
        members: List&lt;String&gt;.from(
          (map['members'] as List&lt;dynamic&gt;),
        ),
      );
    }
  
    String toJson() =&gt; json.encode(toMap());
  
    factory ChatRoom.fromJson(String source) =&gt;
        ChatRoom.fromMap(json.decode(source) as Map&lt;String, dynamic&gt;);
  </code></pre>
  <p><strong>Message Model</strong></p>
  <pre class="lang-dart prettyprint-override"><code>String senderId;
    String content;
    DateTime createdDate;
    List&lt;String&gt; seenBy;
    bool isDeleted;
    FreeLanceMessage({
      required this.senderId,
      required this.content,
      required this.createdDate,
      required this.seenBy,
      required this.isDeleted,
    });
  factory FreeLanceMessage.fromMap(Map&lt;String, dynamic&gt; map) {
      return FreeLanceMessage(
        senderId: map['senderId'] as String,
        content: map['content'] as String,
        createdDate: (map['createdDate'] as Timestamp).toDate(),
        seenBy: List&lt;String&gt;.from(
          (map['seenBy'] as List&lt;dynamic&gt;),
        ),
        isDeleted: map['isDeleted'] as bool,
      );
    }
  
    String toJson() =&gt; json.encode(toMap());
  
    factory FreeLanceMessage.fromJson(String source) =&gt;
        FreeLanceMessage.fromMap(json.decode(source) as Map&lt;String, dynamic&gt;);
  </code></pre>
  <p><a href="https://i.stack.imgur.com/RLGxa.jpg" rel="nofollow noreferrer">Error When the Chat list screen gets mounted to the tree, it only appears for few milliseconds, when I add a new chat, the chat latest message doesn't show an error after this</a></p>
  <p><em>I use random string so don't mind the title and the msg</em></p>
  <p><strong>Edit: I can use streambuilder and check for connection state but the screen blinks for any data change and I don't want that</strong></p>
  <p><strong>so my question is, is this the right way to do this? is there another way? if so, how can I improve this</strong></p>