I am building a one to one chat app using flutter and firebase and i want to display all the chats under label of the day on which it happened,like it is on all major chat apps.No doubt i retrieve data from firestore in ascending order of time by using order by command on the timestamp field of each message.But how to display the day label for every different day.Currently i am using grouped_list package from pub.dev to divide the chat into different groups , but i wish to handle the logic myself , is there any way the same could be achieved by list.view builder in flutter.


Solution 1: Herry

You can split the messages beforehand by yourself. The input would be the List<Message> with the initial timestamp assigned to each message, the output would be a List<List<Message>> where each day is an item in the outer List. The inner List would store the messages for that day, a sample code could look like this:

List<Message> allMessages = await retrieveMessagesFromDataSource();

List<List<Message>> groupedMessages = [];

List<Message> messagesForCurrentDay = [];
DateTime currentDay = allMessages[0].date;

for(Message m in allMessages) {
    if(m.date == currentDay)
        messagesForCurrentDay.add(m);
    else {
        groupedMessages.add(messagesForCurrentDay);
        messagesForCurrentDay = [];
        messagesForCurrentDay.add(m);
        currentDay = m.date;
    }
}


Solution 2: NiklasLehnfeld

For grouping a List in flutter checkout the groupBy method in the collection package.

It works like that:

List<Message> messages = [
  Message(
    time: DateTime.fromMillisecondsSinceEpoch(1597044941000),
    content: "Message 1",
  ),
  Message(
    time: DateTime.fromMillisecondsSinceEpoch(1597044941000),
    content: "Message 2",
  ),
  Message(
    time: DateTime.fromMillisecondsSinceEpoch(1597044941000),
    content: "Message 3",
  ),
  Message(
    time: DateTime.fromMillisecondsSinceEpoch(1596958541000),
    content: "Message 4",
  ),
  Message(
    time: DateTime.fromMillisecondsSinceEpoch(1596958541000),
    content: "Message 5",
  ),
  Message(
    time: DateTime.fromMillisecondsSinceEpoch(1596872141000),
    content: "Message 6",
  ),
  Message(
    time: DateTime.fromMillisecondsSinceEpoch(1596872141000),
    content: "Message 7",
  ),
];

Map<String, List<Message>> grouped =
    groupBy<Message, String>(messages, (message) {
  DateTime time = message.time;
  return "${time.day}.${time.month}.${time.year}";
});

After the grouping you can use the ListView.builder twice. The outer one is for the headlines and the inner one is for the messages.

Like this:

return ListView.builder(
        shrinkWrap: true,
        itemCount: grouped.keys.length,
        itemBuilder: (context, index) {
          String date = grouped.keys.toList()[index];
          List<Message> messages = grouped[date];

          return Column(
            children: [
              Text(
                date,
                style:
                    TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
              ),
              ListView.builder(
                  primary: false,
                  itemCount: messages.length,
                  itemBuilder: (context, index) => Text(
                      "Message: ${messages[index]}",
                      style: TextStyle(
                          color: Colors.black, fontWeight: FontWeight.normal)))
            ],
          );
        }
      );


Solution 3: awaik

In my opinion, previous approaches are not ok for real-world apps that work with data from a backend. We should separate the list with dates dynamically. It is easy and clear to do with a simple check.

Something like this

    ListView.builder(
        reverse: true,
        itemBuilder: (context, int index) {
          /// separate feed by dates
          Widget separator = SizedBox();
          if (index != 0 && messages[index].createdAt.day != messages[index - 1].createdAt.day) {
            separator = Container(
              height: 2,
              width: Get.width,
              color: Colors.black,
            );
          }
          return Column(
            children: [
              separator,
              MessageBubbleWidget(messages[index]),
            ],
          );
        });