Flutter Firebase Firestore

Created At: 2022-12-16 21:00:43 Updated At: 2023-02-18 11:16:46

This article is all about Firebase Firestore tips for data adding, removing, updating, retreiving. It deals with text, image and files. It's about document, document id and retreiving field of a document. We will also cover some information about chatting data. We also covered complex data query using multiple Where() and condition.

If you watch the below videos those are firebase complete tutorial. 

The above app, covers a lot of advanced concept about firebase and firestore.

In this article, I iwll cover textual information about firebase and firestore. We will refer our chatting app. I will extract core ideas of firebase from there and explain bit by bit.

FirebaseFirestore instantiation 

You should have an instance of FirebaseFirestore in your controller or bloc class. You may use Getx or BLoc for this. The idea is same. 

You may have a global variable of FirebaseFirestore in your controller or bloc class like below

 final db = FirebaseFirestore.instance;

Here db will refer out Firestore instance.

How do I get collection data in firebase?

It’s easy to get collection data in Firebase. Just use the get() function with your collection name. Let’s take a look at the code below.

var myData = FirebaseFirestore.instance.get("message").get();

Here we need to use FirebaseFirestore.instance to get a reference of the Firestore instance. Then we mention the collection name.

With the collection name we use the get() function. get() will return all the data from Firestore as documents.

Database structure

Here we will use db to access the firebase data collection, documents and snapshot. Look at our data collection. There are two collections which we will be using through out this tutorial.

Collection data query where()

Once you have collection, you want to query data from collection based on condition. For conditional check we may use where() syntax, where() takes condtions inside.

await db.collection("users").where("id",isNotEqualTo: token).get();

It's more like MySQL database query. If you are familiar mysql database you would know it's super easy.

You may also add multiple where() query with your collection.

 await db.collection("message").where("from_uid", isEqualTo: token).where("to_uid", isEqualTo: to_userdata.id).get();

So if you have conditions, you may just simply add one after another. Inside the where() clause it's upto you what you use as conditions.

Collection withConverter()

You may even add more complex syntax and have better controll with your data query collection. 

For example you may use a convenient function name withConvert() from firebase collection to controll the data type. 

It's important for converting JSON data to object and object to JSON data. Firestore, only takes JSON data. When you make a request to get data, it will return JSON data.

So if you want to submit data you need to convert the data to JSON data, and when you get back data from Firestore then you need to convert data to object using withConvert() function.

You may use withConverter() anywhere right after your collection("message"). Your collection name could be anything.

An example would be

var to_messages = await db.collection("message").withConverter(
      fromFirestore: Msg.fromFirestore,
      toFirestore: (Msg msg, options) => msg.toFirestore(),
    ).where("from_uid", isEqualTo: to_userdata.id).where("to_uid", isEqualTo: token).get();

See in the above example, we are using withConverter() right after collection. If you want and based on your needs you may use between two where() clauses as well.

Save user data in Firebase Collection

We will use GoogleSignInAccount object. This object is available once you try to login using google account to firebase for data storage.

Once you have successfully logged in your, GoogleSignInAccount will hold user's name, email and pictureUrl. This also means we will able to save this data in firebase collection. 

This is what we will do here, take a look at the code below

  Future<void> saveUser(GoogleSignInAccount account) async {
FirebaseFirestore.instance.collection("users")
        .doc(account.email)
        .set({
        "email" : account.email,
        "name": account.displayName,
        "profilepic": account.photoUrl
        });
  }

So here, create a new collection name "users" and save the user infomation from GoogleSignInAccount. First we create a document using account.email and set we used set({}) to insert values in the firebase collection. set({}) takes a Map.

Update User Data in Firebase Collection

Updating user data or any kind of data in firebase is relatively easy. All you need to do call update() method on a certain document.

First you need to find the document ID, and each document should have some fields, call the update() method for that document and update the filed. You may update multiple fields at the same time. 

Update() method takes Map objects

 getFcmToken() async {
   final fcmToken = await FirebaseMessaging.instance.getToken();
   if(fcmToken!=null){
    var user= await db.collection("users").where("id", isEqualTo: token).get();
    if(user.docs.isNotEmpty){
      var doc_id = user.docs.first.id;
      await db.collection("users").doc(doc_id).update({"fcmtoken":fcmToken});
    }
   }
 }

Save Messages in Firebase Collection

Here, we will see how to save chat messages in firebase cloud storage. In general if you store data in firebase, make sure in your collection there are documents. 

The set up I have followed is 

collection-->documents->sub collection->documents

The first collection refers to all the collection name, which in our case is users and then inside this collection we have many documents, each document refers to a user tried to initiate a conversation or chatting.

And each sub-collection refers to a message node. 

and each sub-document refers to each message content.

Detect User Login | Auth Changes

Learn how to detect if the weather has logged in or not flutter firebase. For this we need to listen to auth changes. We would need FirebaseAuth and Stream Objects.

For streaming we would assign Firebase User object as it's type. Stream object could take any type. After that we would assign FirebaseAuth object to Stream object. Stream object is a living object. As long as your alive and uses firebase services with User object, we would be able to listen from it.

  late FirebaseAuth _auth;
  final _user = Rxn<User>();
  late Stream<User?> _authStateChanges;

See at the top, how we declared _auth and _authStateChanges object. Later on we assign _auth.authStateChanges() to Stream object _authStateChanges.

void initAuth() async {
    await Future.delayed(const Duration(seconds: 2)); // waiting in splash
    _auth = FirebaseAuth.instance;
    _authStateChanges = _auth.authStateChanges();
    _authStateChanges.listen((User? user) {
      _user.value = user;
      print("...user id ${user?.email}...");
    });
    navigateToIntroduction();
  }

Firebase Listen for Document Changes

Actually it's relatively easy to listen to document changes in firebase flutter. First all you need to do find a certain to document to listen to.

    final messages = db.collection("message").doc(doc_id).collection("msglist").withConverter(
      fromFirestore: Msgcontent.fromFirestore,
      toFirestore: (Msgcontent msgcontent, options) => msgcontent.toFirestore(),
    ).orderBy("addtime", descending: false);

You see from the code that, here I am finding all the documents in "mgslist", so get all the documents from "msglist" and save it a variable named messages.

Then the messages object will hold all our data, and it will also have snapShots and listen object.

    listener = messages.snapshots().listen(
          (event) {
        print("current data: ${event.docs}");
        print("current data1: ${event.metadata.hasPendingWrites}");

        for (var change in event.docChanges) {
          switch (change.type) {
            case DocumentChangeType.added:
              if(change.doc.data()!=null){

                state.msgcontentList.insert(0,change.doc.data()!);
                SchedulerBinding.instance.addPostFrameCallback((_) {
                  if(myscrollController.hasClients){
                    myscrollController.animateTo(
                      myscrollController.position.minScrollExtent,
                      duration: const Duration(milliseconds: 300),
                      curve: Curves.easeOut,);
                  }
                });
              }
              break;
            case DocumentChangeType.modified:
              print("Modified City: ${change.doc.data()}");
              break;
            case DocumentChangeType.removed:
              print("Removed City: ${change.doc.data()}");
              break;
          }
        }
      },
      onError: (error) => print("Listen failed: $error"),
    );

From the code above you may see how we get snapShots and listen object.

 messages.snapshots().listen((event)=>work to do)

This is the most important steps for listening to changes in documents.

From the event object we can listent o different kind of changes. We simply loop through event.docChanges and find our matches regarding changes.

If you add data, you will be able to listen to 

  case DocumentChangeType.added:

Others follow the same process like removed and modified.

Firebase Firestore Database Structure for Complex App

Retreive common data between users

If you wanna get the common data between two users, then actually it's easy to do. We can use multiple where() and put conditions inside. 

At the same, in the condition you must use users' id to retreive the data. And you have to do it twice, but each time the condition changes. 

In the below example we used the chat record between two users.

See the code below

    var from_messages = await db
        .collection("message")
        .withConverter(
            fromFirestore: Msg.fromFirestore,
            toFirestore: (Msg msg, options) => msg.toFirestore())
        .where("from_uid", isEqualTo: token)  //and condition
        .where("to_uid", isEqualTo: to_userdata.id)
        .get();

    var to_messages = await db
        .collection("message")
        .withConverter(
            fromFirestore: Msg.fromFirestore,
            toFirestore: (Msg msg, options) => msg.toFirestore())
        .where("from_uid", isEqualTo: to_userdata.id)
        .where("to_uid", isEqualTo: token)
        .get();

In the code, we added two where() with conditions which work as AND operator. You also need to take care of from_uid and to_uid value. 

Here from_uid refers to you as user

and to_uid refers to the other person

Firebase get 

Firebase get document by field

Here we will see how Firebase get document by field. To do it, first you need get the collection using doc() and get() functions and save it in a variable. You need to make sure doc() function needs a document id.

And with that result you need to use  data() function. And with data() you need to get a certain property or field from Firebase.

    var result = await db.collection("message").doc(doc_id).get();
    if(result.data()!=null){
      var item = result.data()!["to_name"];
      print(item);
    }

In the above code, message is our collection name and doc_id is the document id, and we used to_name as our document field. 

............coming more...........

Comment

Add Reviews