Real-time Data Management in Flutter with Firebase Database

Samra Khan
10 min readJul 18, 2023

--

Firebase Realtime Database is a cloud-hosted NoSQL database offered by Google as part of the Firebase suite of products. It provides developers with a flexible and scalable solution for building real-time applications that require synchronized data across multiple clients.

Key Features of Firebase Realtime Database

  1. Real-time Data Sync: Firebase Realtime Database allows you to synchronize data in real-time across multiple clients, such as web, mobile, and backend servers. Any changes made to the database are instantly propagated to all connected clients, ensuring consistent and up-to-date data.
  2. NoSQL Database Structure: The Realtime Database uses a NoSQL data model, which means it stores data in a flexible, JSON-like format. This allows for dynamic and hierarchical data structures, making it easy to store and retrieve complex data.
  3. Offline Capabilities: Firebase Realtime Database includes built-in offline support. When a device loses connectivity, the database automatically caches data locally. Once the device reconnects, the local changes are synchronized with the server, ensuring data consistency.
  4. Security and Authentication: Firebase provides robust security rules that allow you to control access to your database. You can define rules based on authentication, user roles, and data validation, ensuring that only authorized users can read and write data.
  5. Scalability and Performance: Firebase Realtime Database automatically scales to handle your application’s needs. It can handle millions of concurrent connections and provides low-latency data access, making it suitable for applications with high traffic and real-time requirements.
  6. Integration with Firebase Services: Firebase Realtime Database seamlessly integrates with other Firebase services, such as Firebase Authentication, Cloud Functions, and Cloud Messaging. This integration allows you to build powerful and complete solutions for user management, serverless functions, and push notifications.
  7. SDKs and Libraries: Firebase Realtime Database offers SDKs for various platforms, including Flutter, iOS, Android, and web. These SDKs provide a simple and consistent API for interacting with the database, making it easy to integrate into your applications.
  8. Monitoring and Analytics: Firebase Realtime Database provides monitoring and analytics features, allowing you to track usage, performance, and errors. You can gain insights into how your application is performing and identify areas for optimization.
  9. Cross-platform Compatibility: Firebase Realtime Database supports a wide range of platforms and programming languages. Whether you’re building a native mobile app, a web application, or a backend service, you can leverage the Realtime Database to manage and synchronize data efficiently.
  10. Easy to Get Started: Firebase Realtime Database offers a straightforward setup process and excellent documentation. You can quickly integrate it into your project using Firebase SDKs, and the Firebase console provides a user-friendly interface for managing your database.

In modern mobile app development, real-time data management is crucial for delivering engaging and dynamic user experiences. Firebase Realtime Database, a NoSQL cloud-based database, offers a convenient solution for syncing data across devices in real-time. In this blog, we will explore how to integrate Firebase Realtime Database into a Flutter application, specifically focusing on curd (create, update, read, delete) operations. We will provide detailed code examples to help you get started with using Firebase Realtime Database in your Flutter projects.

Prerequisites

Before we dive into the implementation, make sure you have the following prerequisites set up:

Step 1: Setting up Firebase Project:

  • Create a new Flutter project using the Flutter command-line tool or your preferred IDE.
  • Navigate to the Firebase console and create a new project.
  • Click on “Add Firebase to your Flutter app” and follow the setup instructions. This will provide you with a google-services.json file that you need to place in your Flutter project's android/app directory.

Step 2: Adding Firebase Dependencies:

  • Open your Flutter project and navigate to the pubspec.yaml file.
  • Add the following dependencies under the dependencies section:
dependencies:
flutter:
sdk: flutter
firebase_core: ^1.4.0
firebase_database: ^11.0.0
  • Run flutter pub get to fetch the newly added dependencies.

Step 3: Initializing Firebase Realtime Database:

  • Open the main.dart file in your Flutter project.
  • Import the necessary Firebase and Flutter packages:
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/firebase_database.dart';
  • Initialize Firebase in the main() function:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}

CRUD Operations with Firebase Realtime Database

Now, let’s dive into the code examples for performing curd operations using Firebase Realtime Database.

Create Operation:

To create a new record in the database, use the push() method to generate a unique key and then set the desired values using the set() method.

void createRecord() {
DatabaseReference databaseReference =
FirebaseDatabase.instance.reference().child('users');

databaseReference.push().set({
'name': 'John Doe',
'email': 'johndoe@example.com',
'age': 30,
});
}

Read Operation:

To retrieve data from the database, use the onValue event listener to receive real-time updates and access the snapshot's DataSnapshot object to extract the data.

void readData() {
DatabaseReference databaseReference =
FirebaseDatabase.instance.reference().child('users');

databaseReference.onValue.listen((event) {
DataSnapshot dataSnapshot = event.snapshot;
Map<dynamic, dynamic> values = dataSnapshot.value;
values.forEach((key, values) {
print('Key: $key');
print('Name: ${values['name']}');
print('Email: ${values['email']}');
print('Age: ${values['age']}');
});
});
}

Update Operation:

To update an existing record, reference the specific record using its unique key and then call the update() method to modify the desired fields.

void updateData() {
DatabaseReference databaseReference =
FirebaseDatabase.instance.reference().child('users');

String recordKey = '-Mfa7Gfrd2Er6q9k7Jbv'; // Replace with your record key

databaseReference.child(recordKey).update({
'name': 'Jane Doe',
'age': 25,
});
}

Delete Operation:

To remove a record from the database, reference the specific record using its unique key and call the remove() method.

void deleteData() {
DatabaseReference databaseReference =
FirebaseDatabase.instance.reference().child('users');

String recordKey = '-Mfa7Gfrd2Er6q9k7Jbv'; // Replace with your record key

databaseReference.child(recordKey).remove();
}

Other Operations with Firebase Realtime Database

Firebase Realtime Database offers several other powerful features and operations that you can utilize in your Flutter application. Let’s explore some of these operations:

  • Search Operation:

To perform a search operation, we can use the orderByChild() method to specify the child property by which we want to search, and then apply the equalTo() method to filter the results based on the desired value.

void searchByName(String name) {
DatabaseReference databaseReference =
FirebaseDatabase.instance.reference().child('users');

databaseReference
.orderByChild('name')
.equalTo(name)
.onValue
.listen((event) {
DataSnapshot dataSnapshot = event.snapshot;
Map<dynamic, dynamic> values = dataSnapshot.value;
values.forEach((key, values) {
print('Key: $key');
print('Name: ${values['name']}');
print('Email: ${values['email']}');
print('Age: ${values['age']}');
});
});
}

In the searchByName() function, we are searching for records where the 'name' property matches the provided value. The results are then printed to the console.

You can call the searchByName() function with the desired name to perform a search operation. For example:

searchByName('John Doe');

This will retrieve and display all the records with the name ‘John Doe’ from the ‘users’ node in the Firebase Realtime Database.

By incorporating the search operation, you can now retrieve specific data based on your search criteria, enabling more advanced data retrieval and filtering capabilities within your Flutter application.

  • Pagination:

When dealing with a large dataset, you can implement pagination to retrieve data in smaller chunks. By using the startAt(), endAt(), and limitToFirst() methods, you can control the number of records to fetch and navigate through the dataset efficiently.

void fetchPaginatedData(int pageSize, String lastRecordKey) {
DatabaseReference databaseReference =
FirebaseDatabase.instance.reference().child('users');

Query query = databaseReference.orderByKey().limitToFirst(pageSize);

if (lastRecordKey != null) {
query = query.startAt(lastRecordKey);
}

query.onValue.listen((event) {
DataSnapshot dataSnapshot = event.snapshot;
Map<dynamic, dynamic> values = dataSnapshot.value;
values.forEach((key, values) {
print('Key: $key');
print('Name: ${values['name']}');
print('Email: ${values['email']}');
print('Age: ${values['age']}');
});
});
}

In this example, the fetchPaginatedData function fetches data from the 'users' node in Firebase Realtime Database in a paginated manner. The pageSize parameter specifies the number of records to retrieve, and lastRecordKey is used to fetch the next page of data.

  • Transactions:

Firebase Realtime Database supports atomic transactions to ensure data integrity in concurrent environments. Transactions allow you to perform multiple read and write operations as a single, indivisible unit. This helps maintain data consistency and avoids conflicts when multiple clients are modifying the same data simultaneously.

void incrementAgeTransaction(String recordKey) {
DatabaseReference databaseReference =
FirebaseDatabase.instance.reference().child('users/$recordKey/age');

databaseReference.runTransaction((MutableData mutableData) async {
if (mutableData.value != null) {
mutableData.value += 1;
}
return mutableData;
});
}

In this example, the incrementAgeTransaction function performs a transaction to increment the 'age' property of a specific record in the 'users' node by 1.

  • Filtering and Sorting:

You can use Firebase Realtime Database queries to filter and sort data based on specific conditions. The orderByChild(), orderByKey(), and orderByValue() methods allow you to sort data by child properties, keys, or values. Additionally, you can combine multiple filters to create complex queries using equalTo(), startAt(), endAt(), and limitToFirst().

void filterAndSortData() {
DatabaseReference databaseReference =
FirebaseDatabase.instance.reference().child('users');

databaseReference
.orderByChild('age')
.startAt(25)
.endAt(40)
.onValue
.listen((event) {
DataSnapshot dataSnapshot = event.snapshot;
Map<dynamic, dynamic> values = dataSnapshot.value;
values.forEach((key, values) {
print('Key: $key');
print('Name: ${values['name']}');
print('Email: ${values['email']}');
print('Age: ${values['age']}');
});
});
}

In this example, the filterAndSortData function filters and sorts the data in the 'users' node based on the 'age' property, retrieving records where the age is between 25 and 40.

  • Presence System:

Firebase Realtime Database provides a presence system that allows you to monitor the online/offline status of users in real-time. You can track user connections, detect when a user goes offline or comes back online, and build features like presence indicators and real-time collaboration.

void setUserOnlineStatus(String userId, bool isOnline) {
DatabaseReference databaseReference =
FirebaseDatabase.instance.reference().child('presence/$userId');

databaseReference.set({
'online': isOnline,
'lastOnline': isOnline ? ServerValue.timestamp : null,
});
}

In this example, the setUserOnlineStatus function sets the online status of a user in the 'presence' node. It updates the 'online' property and, if the user is offline, sets the 'lastOnline' property to the current timestamp.

  • Server-side Operations with Cloud Functions:

You can extend the functionality of Firebase Realtime Database by integrating it with Firebase Cloud Functions. Cloud Functions allow you to write server-side logic triggered by database events or HTTP requests. This enables you to perform complex operations, such as data validation, aggregation, and cross-referencing, on the server.

exports.logNewUser = functions.database
.ref('/users/{userId}')
.onCreate((snapshot, context) => {
const newUser = snapshot.val();
console.log('New user created:', newUser);
return null;
});

In this example, the Cloud Function logs a message when a new record is added to the ‘users’ node.

  • Data Validation and Security Rules:

Firebase Realtime Database allows you to define security rules to control read and write access to your data. You can also add data validation rules to enforce specific data structures, types, and constraints. This ensures that your data remains secure and consistent throughout your application.

{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}

This security rule ensures that only authenticated users can read from and write to the database.

  • Batch Writes:

Firebase Realtime Database supports batch writes, which allow you to perform multiple write operations as a single atomic unit. By grouping multiple updates, inserts, or deletions into a batch, you can ensure that either all the operations succeed or none of them take effect. This is useful for scenarios where you need to maintain data consistency across related data points.

void performBatchWrite() {
DatabaseReference databaseReference =
FirebaseDatabase.instance.reference();

WriteBatch batch = databaseReference.batch();

batch.update(databaseReference.child('users/user1'), {'name': 'Updated Name'});
batch.delete(databaseReference.child('users/user2'));
batch.set(databaseReference.child('users/user3'), {'name': 'New User'});

batch.commit().then((_) {
print('Batch write completed successfully.');
}).catchError((error) {
print('Batch write failed: $error');
});
}

In this example, the performBatchWrite function demonstrates a batch write operation. It updates the 'name' property of 'user1', deletes 'user2', and sets 'user3' with new data. The commit() method executes the batch write, and any errors are handled in the catchError block.

Conclusion

In this comprehensive guide, we explored the powerful capabilities of Firebase Realtime Database and how it seamlessly integrates with Flutter to enable real-time data management in your mobile applications. We started by setting up Firebase in your Flutter project and walked through the process of incorporating Firebase Realtime Database into your app.

We covered the fundamental CRUD operations — create, read, update, and delete — and provided detailed code examples to help you understand how to interact with the database. Additionally, we demonstrated how to implement a search operation to retrieve specific data based on search criteria.

Furthermore, we discussed several advanced operations and features available in Firebase Realtime Database. We explored pagination for handling large datasets, transactions for maintaining data integrity in concurrent environments, filtering and sorting for querying specific data, and the presence system for tracking users’ online/offline status in real-time.

Moreover, we highlighted the ability to leverage server-side operations with Cloud Functions to extend the functionality of Firebase Realtime Database and implement complex server-side logic. We also touched upon the importance of data validation and security rules to ensure the integrity and security of your data.

Lastly, we introduced batch writes as a convenient way to perform multiple write operations as a single atomic unit, reducing the number of server requests and improving efficiency.

Now armed with a solid understanding of Firebase Realtime Database’s features and how to implement them in Flutter, you can take your mobile app development to the next level.

Start leveraging the power of Firebase Realtime Database and Flutter today, and embark on a journey to build engaging and responsive apps that deliver a seamless and interactive user experience. Happy coding!

--

--

Samra Khan

Flutter | Android | IOS | MacOS | Web | Windows | Firebase