Flutter Chat Application (XVIII)

Posted by markmuir on Tue, 04 Jun 2019 21:08:35 +0200

stay Last article In this article, we will implement the basic function of adding chat, but we haven't shown the new chat in the chat list yet. We will show all chats in the chat list.

First, we create a new group_chat_list_body.dart file in the / lib directory. Write a stateful control GroupChatListBody in group_chat_list_body.dart, pass two parameters, phone (mobile phone number) and myName (name), mobile phone number will be used to access "/ chats/$phone" in Firebase real-time database to read the chat list of current users.

import 'package:flutter/material.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_database/ui/firebase_animated_list.dart';
import 'chat_screen.dart';
import 'prompt_wait.dart';

class GroupChatListBody extends StatefulWidget {
  GroupChatListBody({
    this.phone,
    this.myName,
    Key key,
  })
      : super(key: key);
  final String phone;
  final String myName;
  @override
  _GroupChatListBodyState createState() => new _GroupChatListBodyState(phone);
}

class _GroupChatListBodyState extends State<GroupChatListBody> {
  _GroupChatListBodyState(this._phone);
  final String _phone;
  @override
  Widget build(BuildContext context) {
    return new Text("Chat list");
  }
}

Back in the group_chat_list.dart file, modify the build method in _GroupChatListState to use the GroupChatListBody, a stateful control we customized earlier. If the parameter phone is empty, indicating that the user's mobile phone number has not been read, the blank is temporarily displayed until the parameter phone is not empty before the GroupChatListBody control is displayed.

class _GroupChatListState extends State<GroupChatList> {
  //...
  Widget build(BuildContext context) {
    //...
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("Paper chat"),
          centerTitle: true,
          elevation: 0.0,
        ),
        drawer: drawer,
        body: new Center(
          child: phone == "null"
              ? null
              : new GroupChatListBody(phone: phone, myName: name),
        ),
        //...
    );
  }
}

Back in the group_chat_list_body.dart file, we create a new GroupChatListBodyItem class, which is a stateless control to show a specific chat. This control requires a little more parameters, name, lastMessage, timestamp, messages ("/messages/$messages"), myName, myPphone and shePphone.

The top level of GroupChatListBodyItem is a GestureDetector control whose onTap property is an anonymous method for navigating to a new screen whose content is a ChatScreen control. The ChatScreen control is the first screen of the application. We can call it directly by modifying the contents. The specific modifications will be explained in the next article or can be viewed directly on GitHub.

class GroupChatListBodyItem extends StatelessWidget {
  GroupChatListBodyItem(
      {this.name,
      this.lastMessage,
      this.timestamp,
      this.messages,
      this.myName,
      this.myPphone,
      this.shePphone});
  final String name;
  final String lastMessage;
  final String timestamp;
  final String messages;
  final String myName;
  final String myPphone;
  final String shePphone;

  @override
  Widget build(BuildContext context) {
    return new GestureDetector(
        onTap: () {
          Navigator.of(context).push(new MaterialPageRoute<Null>(
            builder: (BuildContext context) {
              return new ChatScreen(
                  messages: messages,
                  myName: myName,
                  sheName: name,
                  myPphone: myPphone,
                  shePphone: shePphone);
            },
          ));
        },
        child: new Container(
            decoration: new BoxDecoration(),
            padding: new EdgeInsets.symmetric(vertical: 4.0, horizontal: 8.0),
            child: new Row(
              children: <Widget>[
                new CircleAvatar(
                    child: new Text(name[0]),
                    backgroundColor: Theme.of(context).buttonColor),
                new Flexible(
                    child: new Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    new Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: <Widget>[
                          new Text("  " + name, textScaleFactor: 1.2),
                          new Text(ReadableTime(timestamp),
                              textAlign: TextAlign.right,
                              style: new TextStyle(
                                  color: Theme.of(context).hintColor)),
                        ]),
                    new Container(
                        padding: new EdgeInsets.only(top: 2.0),
                        child: new Text("  " + lastMessage,
                            overflow: TextOverflow.ellipsis,
                            style: new TextStyle(
                                color: Theme.of(context).hintColor))),
                  ],
                ))
              ],
            )));
  }
}

The previous GroupChatListBodyItem has a timestamp parameter. In Firebase real-time database, it is saved in a timestamp format of "2017-08-1417:16:11.65", not an intuitive readable format, so we need to convert the format. Write a general method ReadableTime in prompt_wait.dart file, convert the given timestamp into intuitive readable form, and add the following code in prompt_wait.dart.

String ReadableTime(String timestamp) {
  List<String> timeList = timestamp.split(" ");
  List<String> times = timeList[1].split(":");
  String time;
  if (new DateTime.now().toString().split(" ")[0] == timeList[0]) {
    if (int.parse(times[0]) < 6) {
      time = "Before dawn${times[0]}:${times[1]}";
    } else if (int.parse(times[0]) < 12) {
      time = "morning${times[0]}:${times[1]}";
    } else if (int.parse(times[0]) == 12) {
      time = "Noon${times[0]}:${times[1]}";
    } else {
      time =
          "Afternoon${(int.parse(times[0])- 12).toString().padLeft(2,'0')}:${times[1]}";
    }
  } else {
    time = timeList[0];
  }
  return time;
}

Override the initState method in _GroupChatListBodyState to enable the control to access "chats/$_phone" in the Firebase real-time database at initialization time. Set Persistence Enabled to true, open the database persistence settings, and call keep Synced (true), so that the data will be automatically downloaded and kept in sync.

class _GroupChatListBodyState extends State<GroupChatListBody> {
  //...
  DatabaseReference _chatsReference;

  @override
  void initState() {
    super.initState();
    _chatsReference = FirebaseDatabase.instance.reference().child('chats/$_phone');
    FirebaseDatabase.instance.setPersistenceEnabled(true);
    _chatsReference.keepSynced(true);
  }
  //...
}

Finally, modify the build method in _GroupChatListBodyState to read the Firebase real-time database using the Firebase AnimatedList control. The parameter query sets the location of the query, the sort sets the sort, which is sorted by the timestamp, and the defaultChild sets the waiting animation when reading. itemBuilder traverses the data queried once and creates a GroupChatListBodyItem control for each of these data.

class _GroupChatListBodyState extends State<GroupChatListBody> {
  //...
  @override
  Widget build(BuildContext context) {
    return new FirebaseAnimatedList(
      query: _chatsReference,
      sort: (DataSnapshot a, DataSnapshot b) =>
          b.value["timestamp"].compareTo(a.value["timestamp"]),
      defaultChild: new CircularProgressIndicator(),
      itemBuilder: (BuildContext context, DataSnapshot snapshot,
          Animation<double> animation) {
        return new SizeTransition(
          sizeFactor: animation,
          child: new GroupChatListBodyItem(
            name: snapshot.value["name"],
            lastMessage: snapshot.value["lastMessage"],
            timestamp: snapshot.value["timestamp"],
            messages: snapshot.value["messages"],
            myName: widget.myName,
            myPphone: _phone,
            shePphone: snapshot.value["phone"],
          ),
        );
      },
    );
  }
}

You can view it directly on GitHub group_chat_list_body.dart file,group_chat_list.dart file and chat_screen.dart file The code.

Topics: snapshot Database Mobile github