Flutter chat app with Cloud Firetore

Cloud Firestore is one of the services of Firebase.
Today, I introduce Chat application of Flutter with Cloud Firestore.

Chat App!! 📱

This is Finished product of Flutter!!

Preview

This introduction is not introduce a login and sign up function.
If you want to know a login and sign up function, I recommend this page.

https://pub.dartlang.org/packages/firebase_auth

Create Development enviroment of Flutter 🔰

Let’s create Flutter development environment of Flutter by looking as following page.


https://flutter.io/setup-macos/

Create Project 😛

Let’s create flutter project!!!

If you have done, you write code of displayed in center “Hello World” of screen.


import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primaryColor: Colors.white
      ),
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Home"),
      ),
      body: new Center(
        child: Text("Hello World"),
      ),
    );
  }
}

Create User name setting screen👦

First, I will create a screen to set up user name.

I think that it is normal to implementation a login authenticate using Firebase Auth afterword, then it seems that there is a chat room list screen afterwords, but I will omit it this time.

I would like to display only user names this time.


import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
          primaryColor: Colors.white
      ),
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Input User name"),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Container(
              margin: EdgeInsets.all(10.0),
              child: TextFormField(
                decoration: InputDecoration(
                    labelText: 'user name'
                ),
              ),
            ),
          RaisedButton(
            color: Colors.blue,
            onPressed: (){},
            child: Text("compleate", style: TextStyle(color: Colors.white),),
          )
        ],
      ),
    );
  }
}

Preview

Next, let’s change the screen here by pushing the setting completion button here!

Create the chat.dart file.

Let’s pass the characters entered as the user name to the next screen and display it in the center of screen.

  • chat.dartlang
import 'package:flutter/material.dart';

class ChatPage extends StatefulWidget {

  ChatPage(this._userName);
  final String _userName;

  @override
  _ChatPageState createState() => new _ChatPageState();
}

class _ChatPageState extends State<ChatPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("chat page"),
      ),
      body: Center(
        child: Text(widget._userName),
      ),
    );
  }
}

And write main.dart the process of screen transition to the page. Let’s write the screen transition process to the tap event of the Button.

import 'package:flutter/material.dart';
import 'package:flutter_chat_app/chat.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
          primaryColor: Colors.white
      ),
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

  final _controller = TextEditingController();
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("Input User name"),
      ),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Container(
            margin: EdgeInsets.all(10.0),
            child: TextFormField(
              controller: _controller,
              decoration: InputDecoration(
                  labelText: 'user name'
              ),
            ),
          ),
          RaisedButton(
            color: Colors.blue,
            onPressed: () {
              Navigator.push(
                  context, MaterialPageRoute(builder: (builder) => ChatPage(_controller.text)));
            },
            child: Text("compleate", style: TextStyle(color: Colors.white),),
          )
        ],
      ),
    );
  }
}

Points can set the TextFormField to controller and obtain the character string entered in controller text.

Create Chat Screen

Now we will create a chat screen.

import 'package:flutter/material.dart';

class ChatPage extends StatefulWidget {

  ChatPage(this._userName);
  final String _userName;

  @override
  _ChatPageState createState() => new _ChatPageState();
}

class _ChatPageState extends State<ChatPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text("chat page"),
      ),
      body: Container(
        margin: const EdgeInsets.symmetric(horizontal: 8.0),
        child: Column(
          children: <Widget>[
            new Flexible(
              child: new ListView.builder(
                padding: new EdgeInsets.all(8.0),
                reverse: true,
                itemBuilder: (_, int index){},
                itemCount: 10,
              ),
            ),
            new Divider(height: 1.0),
            Container(
              margin: EdgeInsets.only(bottom: 20.0, right: 10.0, left: 10.0),
              child: Row(
                children: <Widget>[
                  new Flexible(
                    child: new TextField(
                      decoration: new InputDecoration.collapsed(
                          hintText: "send message"),
                    ),
                  ),
                  new Container(
                    child: new IconButton(
                        icon: new Icon(Icons.send, color: Colors.blue,),
                        onPressed: () {}),
                  ),
                ],
              ),
            ),
          ],
        ),
      )
    );
  }
}

Preview

Connecting Firebase and Flutter🔥

About Firebase connection, you looks following this page.
https://firebase.google.com/docs/flutter/setup?hl=ja

Please proceed to the next step after completing this procedure.

setup of plugin

Since we use Cloud Firestore this time we will install that plugin.
https://pub.dartlang.org/packages/cloud_firestore

Please edit pubspec.yml

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  cloud_firestore: ^0.7.4

Next, in the Firebase console, select Database from the right list and create a database, At that time you will be asked in which mode to make it, so let’s create it in test mode this time.

Create Chat Function

This Code is chat functions.

import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

class ChatPage extends StatefulWidget {
  ChatPage(this._userName);

  final String _userName;

  @override
  _ChatPageState createState() => new _ChatPageState();
}

class _ChatPageState extends State<ChatPage> {
  final _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        appBar: new AppBar(
          title: new Text("chat page"),
        ),
        body: Container(
          margin: const EdgeInsets.symmetric(horizontal: 8.0),
          child: Column(
            children: <Widget>[
              Flexible(
                child: StreamBuilder<QuerySnapshot>(
                  stream: Firestore.instance
                      .collection("chat_room")
                      .orderBy("created_at", descending: true)
                      .snapshots(),
                  builder: (context, snapshot) {
                    if (!snapshot.hasData) return Container();
                    return new ListView.builder(
                      padding: new EdgeInsets.all(8.0),
                      reverse: true,
                      itemBuilder: (_, int index) {
                        DocumentSnapshot document =
                        snapshot.data.documents[index];

                        bool isOwnMessage = false;
                        if (document['user_name'] == widget._userName) {
                          isOwnMessage = true;
                        }
                        return isOwnMessage
                            ? _ownMessage(
                            document['message'], document['user_name'])
                            : _message(
                            document['message'], document['user_name']);
                      },
                      itemCount: snapshot.data.documents.length,
                    );
                  },
                ),
              ),
              new Divider(height: 1.0),
              Container(
                margin: EdgeInsets.only(bottom: 20.0, right: 10.0, left: 10.0),
                child: Row(
                  children: <Widget>[
                    new Flexible(
                      child: new TextField(
                        controller: _controller,
                        onSubmitted: _handleSubmit,
                        decoration:
                        new InputDecoration.collapsed(hintText: "send message"),
                      ),
                    ),
                    new Container(
                      child: new IconButton(
                          icon: new Icon(
                            Icons.send,
                            color: Colors.blue,
                          ),
                          onPressed: () {
                            _handleSubmit(_controller.text);
                          }),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ));
  }

  Widget _ownMessage(String message, String userName) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.end,
      children: <Widget>[
        Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            SizedBox(height: 10.0,),
            Text(userName),
            Text(message),
          ],
        ),
        Icon(Icons.person),
      ],
    );
  }

  Widget _message(String message, String userName) {
    return Row(
      children: <Widget>[
        Icon(Icons.person),
        Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            SizedBox(height: 10.0,),
            Text(userName),
            Text(message),
          ],
        )
      ],
    );
  }

  _handleSubmit(String message) {
    _controller.text = "";
    var db = Firestore.instance;
    db.collection("chat_room").add({
      "user_name": widget._userName,
      "message": message,
      "created_at": DateTime.now()
    }).then((val) {
      print("sucess");
    }).catchError((err) {
      print(err);
    });
  }
}

This is completed.