top of page

Integrating and Using Firestore in Flutter Apps


Integrating and Using Firestore in Flutter Apps

Firestore is a powerful NoSQL database offered by Firebase, a platform provided by Google. It's a perfect fit for building real-time, cloud-hosted applications.


In this article, we'll explore how to integrate Firestore into a Flutter application and build a complete CRUD (Create, Read, Update, Delete) address book application. By the end of this tutorial, you'll have a fully functional address book app that allows you to manage your contacts.


Prerequisites


Before we begin, ensure you have the following prerequisites:

  1. Flutter Environment: Make sure you have Flutter installed and set up on your development machine. You can get started with Flutter by following the official installation guide.

  2. Firebase Account: Create a Firebase account (if you don't have one) and set up a new project on the Firebase Console.

  3. FlutterFire Dependencies: We'll use the cloud_firestore package to interact with Firestore. Add the following dependency to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  cloud_firestore: ^4.9.1

Run flutter pub get to fetch the package.


Setting up Firestore


  1. Firebase Project Configuration: In your Firebase project, go to the Firebase Console and click on "Project settings." Under the "General" tab, scroll down to the "Your apps" section and click on the "Firebase SDK snippet" icon (</>) for the web app. This will provide you with a configuration snippet containing your Firebase credentials.

  2. Initialize Firebase in Flutter: In your Flutter app, open the main.dart file and add the following code to initialize Firebase using the configuration snippet obtained in the previous step:

import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

Building the Address Book App


Now, let's start building our address book app. We'll create a simple app with the following features:

  1. Display a list of contacts.

  2. Add a new contact.

  3. Edit an existing contact.

  4. Delete a contact.

Create a Firestore Collection


In Firestore, data is organized into collections and documents. For our address book app, let's create a collection named "contacts."

final CollectionReference contactsCollection = FirebaseFirestore.instance.collection('contacts');

Create a Model Class


We'll need a model class to represent our contact. Create a file named contact.dart and define the following class:

class Contact {
  final String id;
  final String name;
  final String phoneNumber;

  Contact({required this.id, required this.name, required this.phoneNumber});
}

Create a CRUD Service


Next, let's create a service to perform CRUD operations on our Firestore collection. Create a file named crud_service.dart and implement the following methods:

import 'package:cloud_firestore/cloud_firestore.dart';

class CrudService {
  // Reference to the Firestore collection
  final CollectionReference contactsCollection = FirebaseFirestore.instance.collection('contacts');

  Future<void> addContact(String name, String phoneNumber) async {
    await contactsCollection.add({'name': name, 'phoneNumber': phoneNumber});
  }

  Future<void> updateContact(String id, String name, String phoneNumber) async {
    await contactsCollection.doc(id).update({'name': name, 'phoneNumber': phoneNumber});
  }

  Future<void> deleteContact(String id) async {
    await contactsCollection.doc(id).delete();
  }
}


Implementing UI


Now, let's create the user interface for our address book app using Flutter widgets. We'll create screens for listing contacts, adding a new contact, and editing an existing contact.


Listing Contacts


import 'package:flutter/material.dart';
import 'package:your_app_name/models/contact.dart';
import 'package:your_app_name/services/crud_service.dart';

class ContactListScreen extends StatelessWidget {
  final CrudService crudService = CrudService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Contacts')),
      body: StreamBuilder<QuerySnapshot>(
        stream: crudService.contactsCollection.snapshots(),
        builder: (context, snapshot) {
          if (snapshot.hasError) {
            return Text('Error: ${snapshot.error}');
          }

          if (snapshot.connectionState == ConnectionState.waiting) {
            return CircularProgressIndicator();
          }

          final contacts = snapshot.data?.docs ?? [];

          return ListView.builder(
            itemCount: contacts.length,
            itemBuilder: (context, index) {
              final contact = Contact(
                id: contacts[index].id,
                name: contacts[index]['name'],
                phoneNumber: contacts[index]['phoneNumber'],
              );

              return ListTile(
                title: Text(contact.name),
                subtitle: Text(contact.phoneNumber),
                onTap: () {
                  // Navigate to contact details/edit screen
                },
                onLongPress: () {
                  // Delete contact
                },
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // Navigate to add contact screen
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

Adding and Editing Contacts


import 'package:flutter/material.dart';
import 'package:your_app_name/models/contact.dart';
import 'package:your_app_name/services/crud_service.dart';

class AddEditContactScreen extends StatefulWidget {
  final Contact? contact;

  AddEditContactScreen({this.contact});

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

class _AddEditContactScreenState extends State<AddEditContactScreen> {
  final CrudService crudService = CrudService();
  final _formKey = GlobalKey<FormState>();
  late TextEditingController _nameController;
  late TextEditingController _phoneNumberController;

  @override
  void initState() {
    super.initState();
    _nameController = TextEditingController(text: widget.contact?.name ?? '');
    _phoneNumberController = TextEditingController(text: widget.contact?.phoneNumber ?? '');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.contact == null ? 'Add Contact' : 'Edit Contact'),
      ),
      body: Form(
        key: _formKey,
        child: Column(
          children: [
            TextFormField(
              controller: _nameController,
              decoration: InputDecoration(labelText: 'Name'),
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return 'Please enter a name';
                }
                return null;
              },
            ),
            TextFormField(
              controller: _phoneNumberController,
              decoration: InputDecoration(labelText: 'Phone Number'),
              validator: (value) {
                if (value == null || value.isEmpty) {
                  return 'Please enter a phone number';
                }
                return null;
              },
            ),
            ElevatedButton(
              onPressed: () {
                if (_formKey.currentState!.validate()) {
                  final name = _nameController.text;
                  final phoneNumber = _phoneNumberController.text;

                  if (widget.contact == null) {
                    // Add contact
                  } else {
                    // Update contact
                  }
                }
              },
              child: Text(widget.contact == null ? 'Add Contact' : 'Save Changes'),
            ),
          ],
        ),
      ),
    );
  }
}

Conclusion


In this article, we've walked through the process of integrating Firestore into a Flutter app and building a complete CRUD address book application. You've learned how to set up Firestore, create a model class, implement CRUD operations, and create the user interface for listing, adding, and editing contacts.


This is just the beginning, and you can further enhance your app by adding authentication, search functionality, and more features. Firestore and Flutter provide a powerful combination for building modern and scalable mobile applications.


Happy coding!

Blog for Mobile App Developers, Testers and App Owners

 

This blog is from Finotes Team. Finotes is a lightweight mobile APM and bug detection tool for iOS and Android apps.

In this blog we talk about iOS and Android app development technologies, languages and frameworks like Java, Kotlin, Swift, Objective-C, Dart and Flutter that are used to build mobile apps. Read articles from Finotes team about good programming and software engineering practices, testing and QA practices, performance issues and bugs, concepts and techniques. 

bottom of page