What Is Firebase Cloud Firestore And How To Use It?

By Faheem Ahmad - january 10,2024
What Is Firebase Cloud Firestore And How To Use It? Article

Introduction

Firebase Cloud Firestore is a flexible and scalable NoSQL cloud database for storing and syncing data for client and server-side development. It provides a very easy way to develop applications using flutter which you can target to any platform.

 

Your data is synchronized across all apps through real-time listeners and it also offers offline support for all supported devices, so you can build responsive apps that work regardless of network latency or Internet connectivity.

You can start developing your app quickly with the easy-to-use interface and helpful documentation. Whether you are new or experienced, Flutter with firebase makes it easy to create amazing mobile apps that work across platforms.

Build a simple Firebase Application that will use Firebase CloudFirestore for performing Create, Update, Read, and Delete Functions.

 

The tutorial assumes that your have firebase console already set for the project, You can read our article on Social Login using Firebase here: where we teach you how to use add and confugure firebase with your flutter application, However you should use Firebase CLI as it’s a much faster and easier way to setup the firebase configurations.

Things You are going to learn today

  • Build a simple flutter firebase app that performs CRUD operations.
  • Understand Functions needed to perform CRUD operations.
  • Learn how to create and use models for these operations.
  • Build User Interface for a simple contacts application.
  •  

Things You Need

  • Google Firebase console account
  • Basic understanding of Flutter SDK
  • IDE like VSCode or Android Studio
  •  

Packages required

Open the pubspec.yaml file in your project and add the following dependencies:

				
					cloud_firestore: ^4.13.2
firebase_core: ^2.23.0
				
			

Make sure you change the package version to the latest by going to pub.dev and looking for possible updates.


Now, save the pubsepc.yaml file and run “flutter pub get”  in the terminal to fetch the required packages.

Defining User model

In order for us to structure and manage the data related to a user in your application, we use models as they provide a clear definition of what an instance of a user object should look like.

				
					//user model
//this is model class for user details
class User {
 final String? name, phone, email;
//constructor
 User({required this.name, required this.phone, required this.email});

//initial data
 factory User.initialData() {
   return User(
     name: '',
     phone: '',
     email: '',
   );
 }

 //get data from firebase
 factory User.fromMap(Map<String, dynamic> map) {
   return User(
     name: map['Name'],
     phone: map['Phone'],
     email: map['Email'],
   );
 }

//upload  to firebase
 Map<String, dynamic> toMap() {
   return {
     'Name': name,
     'Phone': phone,
     'Email': email,
   };
 }
}
				
			

In this User model: 

  • The User constructor will create a new user object.
  • The initialData factory constructor will create a new user with initial data. 
  • The fromMap factory constructor will create a new user from a Map<String, dynamic>, which is used for fetching data from Cloud Firestore.
  • The toMap method converts a user object to a Map<String, dynamic>, which is useful when you’re sending data to Cloud Firestore.
Add a User

To add a user, build a basic UI with form-fields according to our model.

Make fields for Username, Email and age with their respective controllers, After that make a function for adding a new user as follows:

				
					 uploadData() async {
   User userDetails = User(
     name: _firstNameController.text,
     phone: _lastNameController.text,
     email: _ageController.text,
   );
   print(
     userDetails.toMap(),
   );
//uploading data to firebase
   await DatabaseMethods.addUserDetails(userDetails.toMap()).then(
       (value) => Fluttertoast.showToast(msg: 'Data uploaded successfully'));
 }


TextButton(
                 onPressed: () {
                  //call upload function on press
                     uploadData();
                   }
                 
                 style: TextButton.styleFrom(
                   foregroundColor: Colors.white,
                   backgroundColor: Colors.blue,
                   disabledForegroundColor: Colors.grey.withOpacity(0.38),
                   minimumSize: const Size(88, 36),
                 ),
                 child: const Text('Submit'),
               )


				
			


Finally make a new file named database_methods.dart. And write a function “addUserDetails()” which will take the userdetails and map them to the firebase cloud Firestore database.

				
					class DatabaseMethods {
 static Future addUserDetails(Map<String, dynamic> userInfoMap) async {  
   //this will create a new document with a random id and add the data to it
    var docRef = FirebaseFirestore.instance.collection('users').doc();
    userInfoMap['id'] = docRef.id; // Add the document ID to the data
    return await docRef.set(userInfoMap);
 }}
				
			

We entered user details and hit enter. The elevatedButton the addUserDetails function which takes the user details as a Map<String, dynamic>, and the name of the user as a string. 

This function interacts with the Firebase Firestore instance ‘users’ collection. It uses the add method to add a new document to the ‘users’ collection with the provided user details. 

Then the add method automatically generates a unique ID for the new document and returns a Future<DocumentReference> that completes when the write finishes.

If the write is successful, the new user details are stored in the Firestore database. If an error occurs during the write, the returned Future completes with an error.

View all Users

Now that our first user has been added to firebase firestore, let’s show the users by fetching that user and showing their details.

				
					class ViewAllUsers extends StatefulWidget {
 const ViewAllUsers({super.key});
 @override
 _ViewAllUsersState createState() => _ViewAllUsersState();
}

class _ViewAllUsersState extends State<ViewAllUsers> {
 List<User> userDetailsList = [];

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

 Future<void> getAllUsers() async {
   QuerySnapshot value = await DatabaseMethods.fetchAllUsers();
   userDetailsList = value.docs
       .map((e) => User.fromMap(e.data() as Map<String, dynamic>))
       .toList();
   setState(() {});}

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(title: const Text('View All Users')),
     body: ListView.builder(
       itemCount: userDetailsList.length,
       itemBuilder: (context, index) {
         final contactList = userDetailsList[index].toMap();
         return ListTile(
           title: Text(contactList['Name']),
           trailing: Row(
             mainAxisSize: MainAxisSize.min,
             children: [
               IconButton(
                 onPressed: () {
                 },
                 icon: const Icon(Icons.delete_rounded),  ),
               IconButton(
                 onPressed: () {                   },
                 icon: const Icon(Icons.edit_rounded),
               ), ],),
           subtitle: Text(contactList['Email']), );},
     ), }}



class DatabaseMethods {  //this will return all the users
 static Future<QuerySnapshot> fetchAllUsers() async {
   return await FirebaseFirestore.instance.collection('users').get();}}


				
			

We created a list of User type model “User” and named it userDetailsList, and created a function that fetches all users in the Firestore database and converts it into a list of User objects.

Then we defined and called the getAllUsers function in the initState method.

The getAllUsers() function fetches all users from a Firestore database and maps them into a list of User objects.

After fetching and mapping the data, it triggers a UI update.

Let’s talk about the UI that shows the fetched user, The UI code is building a list of users fetched from a database.

Each user is represented as a ListTile in a ListView.builder, It takes the length of the userDetailsList as the itemCount and builds a ListTile for each user.

Each ListTile displays the user’s name as the title and email as the subtitle. It also includes two trailing icons for deleting and editing the user (which we will talk about in the next section).

Delete User

So far we have learned how to add a new user and then display it. It’s time to delete the user. Let’s write the code for deleting a user.

First Define a function for deleting a user, deleteUser()

				
					 //this will delete the user
 static Future<void> deleteUser(String id) async {
   return await FirebaseFirestore.instance
       .collection('users')
       .where(id, isEqualTo: id)
       .get()
       .then((value) {
     for (var element in value.docs) {
       element.reference.delete();
     }
   });
 }

				
			

This deleteUser function deletes a user from the ‘users’ collection in Firestore. It takes the user’s ‘id’’ as input, finds specific documents where the ‘id ’field matches the input name, and deletes that document.

 

And in the UI, just simply update the onPressed property to of the button to following:

				
					                                 IconButton(     //Icon button for Deleting user 
                                 onPressed: () {
                                   setState(() {
                                     DatabaseMethods.deleteUser(
                                         contactList['id']);
                                     userDetailsList.removeAt(index);
                                   });
                                 },
                                 icon: const Icon(
                                   Icons.delete_rounded,
                                   size: 30,
                                 ),
                               ),
				
			

This calls the deleteUser method from DatabaseMethods class, and passes the name of the user from contactList as an argument, Which deletes the user from firebase cloud firestore, also removes the user from the local userDetailsList at the current index for UI.\

Let’s test our code in in the app:

Now that we have removed the user named ‘farhan’, It’s no longer showing in the app as well as the firebase console.

Update/Edit User

To update the user let’s first make a function that takes an object of the user and update it.

				
					//update user details
 static Future<void> updateUserDetails(
     String id, Map<String, dynamic> updatedData) async {
   return await FirebaseFirestore.instance
       .collection('users')
       .doc(id)
       .update(updatedData);
 }
				
			

This function updateUserDetails() takes two parameters: 

  • id 
  • updatedData

The function updates the document in the ‘users’ collection of a Firestore database.

First the  document to be updated is identified by the id, Then the update method is used to update the document with the data provided in updatedData.

 

Finally this function returns a Future<void> which completes when the update operation is finished.


Now for the user interface add the following code for the button in view_all_users.dart

				
					                           IconButton(
                                 onPressed: () async {
                                   Navigator.push(
                                     context,
                                     MaterialPageRoute(
                                       builder: (context) => EditContactPage(
                                         contact: userDetailsList[index],
                                       ),
                                     ),
                                   );
                                 },
                                 icon: const Icon(
                                   Icons.edit_rounded,
                                   size: 30,
  ),
 ),

				
			

This will pass the  corresponding user object to the EditContactPage for editing.

Now let’s create a new screen for edit contact page:

				
					// Page for editing user contact details
class EditContactPage extends StatefulWidget {
 final User contact;
 const EditContactPage({Key? key, required this.contact}) : super(key: key);

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

class _EditContactPageState extends State<EditContactPage> {
 // Controllers for user details text fields
 final _name = TextEditingController();
 final _phone = TextEditingController();
 final _email = TextEditingController();

 @override
 void initState() {
   // Initialize text fields with current user details
   _name.text = widget.contact.name!;
   _phone.text = widget.contact.phone!;
   _email.text = widget.contact.email!;
   super.initState();
 }

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(title: const Text('Edit Contact')),
     body: Padding(
       padding: const EdgeInsets.all(20),
       child: Column(
         children: [
           _buildTextField(_name, 'First Name'),
           const SizedBox(height: 20),
           _buildTextField(_phone, 'Phone Number'),
           const SizedBox(height: 20),
           _buildTextField(_email, 'Email'),
           const SizedBox(height: 20),
           _buildSaveButton(),
         ],
       ),
     ),
   );
 }

 // Helper function to build text fields
 TextFormField _buildTextField(
     TextEditingController controller, String label) {
   return TextFormField(
     controller: controller,
     decoration: InputDecoration(
       border: const OutlineInputBorder(),
       labelText: label,
     ),);}

 // Helper function to build save button
 ElevatedButton _buildSaveButton() {
   return ElevatedButton(
     onPressed: () async {
       User updatedUserDetails = User(
         id: widget.contact.id,
         name: _name.text,
         phone: _phone.text,
         email: _email.text,
       );
       await DatabaseMethods.updateUserDetails(
           updatedUserDetails.id!, updatedUserDetails.toMap());
     },
     child: const Text('Save',
         style: TextStyle(fontSize: 20, color: Colors.white)),
   );}}


				
			

In this class, we are taking a user object in parameters and then in the inItState() method we are initializing the user details in the form field controllers.

Now when the save button is pressed, a new user object is created with the changes made by the user to the original user data, then we pass this object to user id to the updateUserDetails

method we defined earlier.

Let’s take a look at the application on the phone as we edit a user details:

Congratulations! You have built your first firebase CRUD application.

Conclusion

Today you learned how easy it is to build your very own Flutter Application where you can create a user, remove a user, update its details and show all users.

You first started out with a default flutter project, then you learned how to build a User Interface that is simple and clear.

You learned how to write functions for CRUD operations and how to call them.

If this article has helped in any way and you have learned something new today, Kindly consider sharing the article so others can also benefit and learn how to build simple AI applications.

Subscribe to our website for more interesting articles on AI and other interesting fields of IT.

You can find the code for this here:

Repository Link:

Written By
Faheem Ahmad


wpChatIcon
wpChatIcon