Education
Software Development Executive - II
Last updated on May 30, 2024
Last updated on Aug 12, 2023
Let's start the explorative journey into the world of CRUD operations in Flutter using Firebase. We'll also learn about Flutter GetX Firebase CRUD.
Flutter, as many of you know, has become a popular framework for creating cross-platform apps, ensuring seamless performance with wonderful UI. Its compatibility with Firebase brings in an abundance of options to perform CRUD operations, which are essential for any database-driven application. In this guide, we'll harness the power of Firebase and the efficiency of Flutter's GetX package to smoothly perform CRUD operations. We'll take one example app and guide you step by step through the whole process.
Our first task is to set up our workspace. We'll begin by creating a new Flutter app and preparing our dart files. Let's kick off by creating a new Flutter application:
1 flutter create crud_in_flutter 2 3 cd crud_in_flutter 4
Following the creation of the Flutter application, we need to focus on setting up our Dart files. The backbone of any Flutter application lies within the Dart files, and for our CRUD operations, we need them set up appropriately.
In your lib folder, create a new Dart file which will hold our Model Class (we'll cover Model Class in the coming section).
1 lib/models/user_model.dart 2
Now that the Flutter application is set, and our Dart files are in place, we are ready to integrate Firebase into our application. However, before we move towards Firebase, ensure your files in the Flutter project are arranged accordingly.
Integrating Firebase is an integral part of our Flutter CRUD API. Firebase provides us with a real-time database, intuitive APIs, authentication, and easy access among other features. But before we lace our Flutter app with Firebase, kindly ensure you have a Firebase account, a new project, and ready access to it.
For us to integrate Firebase into our Flutter project, follow these steps:
1 dependencies: 2 flutter: 3 sdk: flutter 4 5 firebase_core: ^2.15.0 6 cloud_firestore: ^4.8.4 7 firebase_auth: ^4.7.2 8
Then, we'll run flutter pub get command to get all the necessary packages for our app.
Next, we initialise Firebase in our main() function.
1 void main() async { 2 WidgetsFlutterBinding.ensureInitialized(); 3 await Firebase.initializeApp(); 4 runApp(MyApp()); 5 } 6
In this section, we are going to define one model class User, comprising of two variables 'string name' and 'string email'. The model class is core to any data-driven Flutter application, assisting us in handling and manipulating the received data effectively.
Let's dive into building our model class:
1 class User { 2 String name; 3 String email; 4 5 User({this.name, this.email}); 6 7 // Converting User details to a Map 8 Map<String, dynamic> toMap() { 9 return { 10 'name': name, 11 'email': email, 12 }; 13 } 14 15 // Extracting User details from Map 16 User.fromFirestore(Map<String, dynamic> firestore) 17 : name = firestore['name'], 18 email = firestore['email']; 19 } 20
Our User model class has two methods:
toMap(): This function transforms our User class attributes into a map, easing the process of updating and adding user data to Firebase.
fromFirestore(): It helps in extracting data from Firestore to be used in our Flutter app.
In each method, we have used Map<String, dynamic>
which indicates that we have a map of multiple data types.
Following the setup of our model classes, we're ready to dive into CRUD operations using Firebase. CRUD stands for Create, Read, Update, and Delete, operations that Firebase provides right off the shelf for us to utilize in our projects with ease. By performing these operations, we can manage data in our data-driven Flutter application.
Our first task is to create functions for CRUD operations in a separate dart file. Let's name the dart file user_repository.dart. The UserRepository class will use the cloud_firestore package to interact with Firestore.
1 import 'package:cloud_firestore/cloud_firestore.dart'; 2 import 'models/user_model.dart'; 3 4 class UserRepository { 5 final FirebaseFirestore _firestore; 6 UserRepository(this._firestore); 7 } 8
You will add the create, read, update and delete functions inside this UserRepository class as we aim to manage user documents within Firebase.
Guiding you through the process and how we will be executing different operations, starting from Create function, the Read function proceeds, followed by the Update function, and ending with the Delete function in Firestore using a Flutter GetX CRUD API is our next step.
Let's begin by adding a 'create' function in the UserRepository class to create a new user in the Firebase Firestore. Execute the addition of data by calling the add function on the Firestore instance.
1 Future<void> createUser(User user) async { 2 await _firestore.collection('users').add( 3 user.toMap(), 4 ); 5 } 6
The 'createUser' function will accept User type data as a parameter. We then use the toMap() method to convert user data into a format suitable for Firestore. Now, your application is ready to create new users and add data to Firebase Firestore.
It's worth mentioning here that you should handle exceptions and errors as per your project's requirements.
After successfully executing the create operation, now it's time to fetch or "read" our data from Firebase. Establishing the "read" operation goes hand in hand with building our user interface, so we can display the data gathered from Firestore in the app.
For reading data from Firebase Firestore, we will add a 'read' function in the UserRepository class. This function will call the get function on the Firestore instance.
1 Future<List<User>> readUsers() async { 2 QuerySnapshot querySnapshot = await _firestore.collection('users').get(); 3 4 return querySnapshot.docs.map((doc) => User.fromFirestore(doc.data())).toList(); 5 } 6
This function fetches all user documents from Firestore, converts each document to a User object, and returns a List of these User objects. Keep in mind, the user interface and the displaying of data will come later in our build process.
Moving forward with the update operation, also known as 'edit' operation, we are going to create a function in the UserRepository class that will update specific data in Firebase Firestore. This function helps in modifying an existing user's data.
For this task, we will be adding the following updateUser() function:
1 Future<void> updateUser(String userId, User user) async { 2 await _firestore.collection('users').doc(userId).update( 3 user.toMap(), 4 ); 5 } 6
In the code above, updateUser() requires two parameters: the ID of the existing user userId, which can be fetched from Firestore, and the User object user that contains updated user information.
Please remember to catch potential exceptions and handle errors that might occur during the updating process.
Wrapping up our CRUD operations, the final operation to implement is the 'Delete Operation'. This will allow us to delete a specific user from our Firebase Firestore.
In the UserRepository class, we'll add a new function deleteUser(). Below is the code that shows how we delete data from Firebase Firestore.
1 Future<void> deleteUser(String userId) async { 2 await _firestore.collection('users').doc(userId).delete(); 3 } 4
In the code snippet above, deleteUser function requires the ID of the user that we want to delete, and .delete() function from Firestore is called in order to remove the specified user.
And with that, we have successfully established all four CRUD operations, forming our Flutter GetX CRUD API. Next, we are going to explore how managing these functions and states becomes easier with the help of the GetX package.
Having set up our CRUD operations, now's the right time to talk about managing these functionalities effectively. Enter GetX - a high-performance, lightweight dependency injection & state management system for managing your Flutter app's UI.
Firstly, add get in the dependencies section of the pubspec.yaml file.
1 dependencies: 2 get: ^4.6.5 3
Now, we need to modify the main.dart file to include GetMaterialApp.
1 void main() async { 2 WidgetsFlutterBinding.ensureInitialized(); 3 await Firebase.initializeApp(); 4 5 runApp(GetMaterialApp( 6 home: HomeScreen(), //We will define the HomeScreen later. 7 )); 8 } 9
We will define a class UserRepositoryController which will handle the state of the UserRepository:
1 class UserRepositoryController extends GetxController { 2 UserRepository _userRepository; 3 RxList<User> users; 4 5 UserRepositoryController() { 6 _userRepository = UserRepository(FirebaseFirestore.instance); 7 users.bindStream(_userRepository.getUsersStream()); 8 } 9 ... 10 ... 11 } 12
With this GetX setup, we are ready to apply it for managing and triggering our CRUD operations within the app.
Let's put our CRUD operations into play now. For this, we first need to build the UI of our app. We will create a very simple UI with a list of users, and options to create, update and delete users.
We are not going into the UI part in detail here, but you can always design it in your style.
First, let's create the HomeScreen. Here, we will take UserRepositoryController as a dependency:
1 class HomeScreen extends StatelessWidget { 2 @override 3 Widget build(BuildContext context) { 4 final userRepositoryController = Get.put(UserRepositoryController()); 5 6 return Scaffold( 7 ... 8 ); 9 } 10 } 11
Before testing our CRUD operations, let's ensure that we have incorporated proper error handling in our implementation. When making HTTP requests, an exception can be thrown. We want to make sure these exceptions are caught and handled cleanly.
We can now run our Flutter application and perform the CRUD operations. Try to add data to Firestore, read it, update it, and delete it using the functions we created. If all goes as planned, we will successfully manipulate the data in Firestore reflected in our Flutter app.
As our Flutter CRUD API is in a good shape, we could spare a thought for scaling it. As our project grows, it becomes increasingly important to adhere to the 'Clean Architecture' principles. It makes your code modular, scalable, and maintainable, which results in fewer headaches down the road.
The UserRepository that we created is the primary example of applying Clean Architecture. We separated our data layer from our UI layer, decoupling our classes and making the app architecture neat, modular, and scalable.
Speaking of updates, you might want to invest some thought into securing your application, adding more types of users with different access levels, and more. Stay updated with the official Flutter website for updates and enhancements that can further boost your CRUD operations.
It's essential to know that programming comes with its fair share of errors and Flutter is no exception. Don't be discouraged when you encounter errors. Instead, understand that errors are a part of the learning process and often teach us a lot.
Let's discuss some common errors you might encounter while using Firebase and CRUD operations:
While errors vary from coding errors to conceptual misunderstandings, the solution is always better understanding and debugging. Flutter’s extensive logging during debug sessions can be immensely helpful when you're trying to solve these problems.
Hats off to you for coming this far! Today, we've achieved quite a milestone. We navigated through the process of building a CRUD API using Firebase in a Flutter application. The journey involved setting up Flutter and Firebase, creating Model Classes, and implementing Create, Read, Update, and Delete operations. We also familiarized ourselves with the power of GetX for state management and covered some common errors that might come up along the way.
As always, the learning doesn't stop here. Flutter and Firebase are both vast and versatile, continually growing with frequent updates and countless possibilities. All the coding principles and practices you learned here will serve as a robust foundation for any data-driven application you're planning to develop.
To make this tedious process smoother there arrives an exciting Flutter development toolkit that can make your workflow even more efficient and productive - WiseGPT. WiseGPT is a revolutionary plugin designed to generate code for APIs directly into your Flutter project, with no limit on the output size. Its unique abilities mirror your coding style, making it feel like an extension of your own coding prowess.
So why not take your Flutter development to the next level? Try out WiseGPT and experience the ease and efficiency of automated API code generation. Simply provide a collection of APIs, and WiseGPT will handle the rest, making your development process smoother and more enjoyable.
I hope you found this guide as exciting and helpful as it was meant to be. Keep exploring, let's rewrite the world of cross-platform apps, and don't forget to explore the power of WiseGPT in your future Flutter projects!
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.