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.
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...........