londonappbrewery / Flash-Chat-Flutter-Complete

The completed code for the Flash Chat Project - The Complete Flutter Development Bootcamp
https://www.appbrewery.co/
92 stars 133 forks source link

Message sorting not working as intended. #1

Open ak2766 opened 4 years ago

ak2766 commented 4 years ago

Just wanted to state that my final chat_screen code is exactly identical to your final project but the sorting of messages isn't working as intended - both on the emulator and on my Galaxy S8+. See screenshots below. There's something very odd happening here.

Will try and fix it on my own and report back here.

I've included a number (1-8) either at the beginning or at the end of the chat message to allow you to follow message post chronology.

Emulator: Selection_00182

Galaxy S8: Selection_00183

ak2766 commented 4 years ago

After thinking this through a little bit before embarking on coding (just by looking at the order in which they appear in Firebase), it seems the real solution is to add a timestamp to each message, then on retrieval, sort the messages based on the timestamp before adding them to the messageBubbles list.

FadyWaheed11 commented 4 years ago

can you share the code after your update @ak2766

koorosh-k98 commented 4 years ago

I added timestamp into the code and it worked. @FadyWaheed11 chat_screen.txt

farzintava commented 4 years ago

I used DateTime into the code and it works well. chat_screen.txt

ak2766 commented 4 years ago

@FadyWaheed11

can you share the code after your update @ak2766

Apologies, I got busy and haven't returned to this, but I see others had the same idea and already have a solution. Hopefully that works for you too.

FadyWaheed11 commented 4 years ago

Thanks bro it works @ak2766

Elsrougy commented 4 years ago

Hello guys, I know it's been a while but can I add something?

With the method you proposed, if someone with a phone clock which is not accurate it will mess up the order, right? I know that will be rare but it's something we should think about.

farzintava commented 4 years ago

Hello guys, I know it's been a while but can I add something?

With the method you proposed, if someone with a phone clock which is not accurate it will mess up the order, right? I know that will be rare but it's something we should think about.

If use unix time instead of phone clock it works well.

Raj-kar commented 4 years ago

Use This Code after clearing your all old Database message and Data from firebase cloud DataBase . Once you clear all the previous data from database, you can you this code . credit :- @koorosh-k98,

`import 'package:flutter/material.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import '../constants.dart';

final _firestore = Firestore.instance; FirebaseUser loggedInUser;

class ChatScreen extends StatefulWidget { static const String id = 'chat_screen'; @override _ChatScreenState createState() => _ChatScreenState(); }

class _ChatScreenState extends State { final messageTextController = TextEditingController(); final _auth = FirebaseAuth.instance;

String messageText;

@override void initState() { super.initState();

getCurrentUser();

}

void getCurrentUser() async { try { final user = await _auth.currentUser(); if (user != null) { loggedInUser = user; } } catch (e) { print(e); } }

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: null, actions: [ IconButton( icon: Icon(Icons.close), onPressed: () { _auth.signOut(); Navigator.pop(context); }), ], title: Text('⚡️Chat'), backgroundColor: Colors.lightBlueAccent, ), body: SafeArea( child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ MessagesStream(), Container( decoration: kMessageContainerDecoration, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: TextField( controller: messageTextController, onChanged: (value) { messageText = value; }, decoration: kMessageTextFieldDecoration, ), ), FlatButton( onPressed: () { messageTextController.clear(); _firestore.collection('messages').add({ 'text': messageText, 'sender': loggedInUser.email, 'date and time': DateTime.now().toString(), }); }, child: Text( 'Send', style: kSendButtonTextStyle, ), ), ], ), ), ], ), ), ); } }

class MessagesStream extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( stream: _firestore.collection('messages').snapshots(), builder: (context, snapshot) { if (!snapshot.hasData) { return Center( child: CircularProgressIndicator( backgroundColor: Colors.lightBlueAccent, ), ); } final messages = snapshot.data.documents.reversed; List messageBubbles = []; for (var message in messages) { final messageText = message.data['text']; final messageSender = message.data['sender']; final messageDateTime = message.data['date and time'];

      final currentUser = loggedInUser.email;

      final messageBubble = MessageBubble(
        sender: messageSender,
        text: messageText,
        isMe: currentUser == messageSender,
        dateTime: messageDateTime,
      );

      messageBubbles.add(messageBubble);
       messageBubbles.sort((a, b) =>
          DateTime.parse(b.dateTime).compareTo(DateTime.parse(a.dateTime)));
    }
    return Expanded(
      child: ListView(
        reverse: true,
        padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0),
        children: messageBubbles,
      ),
    );
  },
);

} }

class MessageBubble extends StatelessWidget { MessageBubble({this.sender, this.text, this.isMe, this.dateTime});

final String sender; final String text; final String dateTime; final bool isMe;

@override Widget build(BuildContext context) { print(dateTime); return Padding( padding: EdgeInsets.all(10.0), child: Column( crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ Text( sender, style: TextStyle( fontSize: 12.0, color: Colors.black54, ), ), Material( borderRadius: isMe ? BorderRadius.only( topLeft: Radius.circular(30.0), bottomLeft: Radius.circular(30.0), bottomRight: Radius.circular(30.0)) : BorderRadius.only( bottomLeft: Radius.circular(30.0), bottomRight: Radius.circular(30.0), topRight: Radius.circular(30.0), ), elevation: 5.0, color: isMe ? Colors.lightBlueAccent : Colors.white, child: Padding( padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0), child: Text( text, style: TextStyle( color: isMe ? Colors.white : Colors.black54, fontSize: 15.0, ), ), ), ), ], ), ); } } `

Heipi commented 4 years ago

Expanded( child: TextField( controller: messageTextController, onChanged: (value) { messageText = value; },

very good,Thanks

RahiPatel25 commented 4 years ago

TimeStamp or DateTime is not proper method to sorting messages because i give one example, So there are two Devices, DeviceA and DeviceB both install this App but DeviceA's time is 10:45 and DeviceB's time is 10:50 so messages not sorting properly, my suggestion is to use this below method to work properly...

onPressed: () { messageTextController.clear(); _firestore.collection('messages').add({ 'text': messageText, 'sender': loggedInUser.email, 'timestamp': FieldValue.serverTimestamp(),

                    });
                  },

class MessagesStream extends StatelessWidget { @override Widget build(BuildContext context) { return StreamBuilder( stream: _firestore .collection('messages') .orderBy('timestamp', descending: false) .snapshots(), // ignore: missing_return builder: (context, snapshot) { if (!snapshot.hasData) { return Center( child: CircularProgressIndicator( backgroundColor: Colors.lightBlueAccent, ), ); }

( Highlight of Sorting method above code:- 'timestamp': FieldValue.serverTimestamp(),

_firestore .collection('messages') .orderBy('timestamp', descending: false) .snapshots(), ) I used this this methos and its work properly, I hope this will help you! Thank you

jjjvvvvv commented 3 years ago

@RahiPatel25 Thank you. This fixed the sorting issue as well as an issue with messages grouping together out of order, AND by the sender. For the sake of making your comment a little easier to read I will include the code that worked for me below...

The two major components being:

'timestamp': FieldValue.serverTimestamp(),

and

.orderBy('timestamp', descending: false)

In your chat_screen.dart file...


                    onPressed: () {
                      messageTextController.clear();
                      _firestore.collection('messages').add({
                        'text': messageText,
                        'sender': loggedInUser.email,
                        'timestamp': FieldValue.serverTimestamp(),
                      });
                    },
                    child: Text(
                      'Send',
                      style: kSendButtonTextStyle,
                    ),
                  ),

// further down in your code...

return StreamBuilder<QuerySnapshot>(
      stream: _firestore
          .collection('messages')
          .orderBy('timestamp', descending: false)
          .snapshots(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return Center(
            child: CircularProgressIndicator(
              backgroundColor: Colors.lightBlueAccent,
            ),
          );
ygzkrmtc commented 3 years ago

I even could not open, when I run code, and tried to open on my phone it stoppes doesnt open why this could be?

suprimpoudel commented 3 years ago

Flutter has changed many things over the past few years along with class names and different objects. Use this code to sort the most recent messages import 'package:flutter/material.dart'; import 'package:chat_application/constants.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:cloud_firestore/cloud_firestore.dart';

final _fireStore = FirebaseFirestore.instance; final _auth = FirebaseAuth.instance; User loggedInUser;

class ChatScreen extends StatefulWidget { static const String id = 'chat_screen';

@override _ChatScreenState createState() => _ChatScreenState(); }

class _ChatScreenState extends State { final messageTextController = TextEditingController();

String messageText;

@override void initState() { super.initState(); getCurrentUser(); }

void getCurrentUser() async { try { final user = _auth.currentUser; if (user != null) { loggedInUser = user; } } catch (e) { print(e); } }

void messagesStream() async { await for (var snapshot in _fireStore.collection('messages').snapshots()) { for (var message in snapshot.docs) { print(message.data()); } } }

@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: null, actions: [ IconButton( icon: Icon(Icons.power_settings_new), onPressed: () { messagesStream(); _auth.signOut(); Navigator.pop(context); }, ), ], title: Text('⚡️Chat'), backgroundColor: Colors.lightBlueAccent, ), body: SafeArea( child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ MessagesStream(), Container( decoration: kMessageContainerDecoration, child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: TextField( controller: messageTextController, onChanged: (value) { messageText = value; }, decoration: kMessageTextFieldDecoration, ), ), TextButton( onPressed: () { messageTextController.clear(); try { _fireStore.collection('messages').add( { 'sender': loggedInUser.email, 'text': messageText, 'date and time': DateTime.now().toString(), }, ); } catch (e) { print(e); } }, child: Text( 'Send', style: kSendButtonTextStyle, ), ), ], ), ), ], ), ), ); } }

class MessagesStream extends StatelessWidget { const MessagesStream({Key key}) : super(key: key);

@override Widget build(BuildContext context) { return StreamBuilder( stream: _fireStore.collection('messages').snapshots(), // ignore: missing_return builder: (context, snapshot) { if (!snapshot.hasData) { return Center( child: CircularProgressIndicator( backgroundColor: Colors.blue, ), ); } final messages = snapshot.data.docs.reversed; List messageList = []; for (var message in messages) { final messageText = message.get('text'); final messageSender = message.get('sender'); final dateTime = message.get('date and time'); print(_auth.currentUser.email); final messageBubble = MessageBubble(messageText, messageSender, _auth.currentUser.email == messageSender, dateTime); // currentUser == messageSender messageList.add(messageBubble); messageList.sort( (a, b) => DateTime.parse(b.dateTime).compareTo( DateTime.parse(a.dateTime), ), ); } return Expanded( child: ListView( reverse: true, padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 20.0), children: messageList, ), ); }, ); } }

class MessageBubble extends StatelessWidget { final String sender; final String text; final String dateTime; final bool isMe; MessageBubble(this.text, this.sender, this.isMe, this.dateTime); @override Widget build(BuildContext context) { return Padding( padding: EdgeInsets.all(10.0), child: Column( crossAxisAlignment: isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ Text( sender, style: TextStyle( color: Colors.black54, fontSize: 12.0, ), ), Material( color: isMe ? Colors.lightBlueAccent : Colors.white, elevation: 5.0, borderRadius: isMe ? BorderRadius.only( topLeft: Radius.circular(30.0), bottomLeft: Radius.circular(30.0), bottomRight: Radius.circular(30.0), ) : BorderRadius.only( topLeft: Radius.circular(30.0), topRight: Radius.circular(30.0), bottomRight: Radius.circular(30.0), ), child: Padding( padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 20.0), child: Text( text, style: TextStyle( color: isMe ? Colors.white : Colors.black, fontSize: 15.0, ), ), ), ), ], ), ); } }

besharman commented 3 years ago

Hey I'm working on an almost same app, the order is still very unpredictable even with those two reverse conditions.

Abdusamad98 commented 3 years ago

Great , it works ,and me also Thanks guys