Firebase and Firestore
Learning objectives
- You know what Firestore is.
- You know how to create a Firestore project.
At this point, we have a basic note-taking application that allows typing in notes, listing them, and deleting them. The data is not stored anywhere yet, however. Here, we'll look into working with Firestore to store the data in the cloud.
Starting with Firebase
To get started with Firebase, follow the Add Firebase to your Flutter app guidelines in the Flutter documentation at https://firebase.google.com/docs/flutter/setup. To summarize, you need to:
- Sign into Firebase Console and create a Firebase project
- Install the Firebase CLI
- Login to Firebase with the CLI using
firebase login
- install FlutterFire CLI by running
dart pub global activate flutterfire_cli
- Configure FlutterFire for the Flutter project by running
flutterfire configure
in the root folder of the Flutter project -- among other things, this creates a file calledfirebase_options.dart
to thelib
folder - Add the
firebase_core
package to the Flutter project and re-configure the project by runningflutterfire configure
again -- this can be done by callingflutter pub add firebase_core
and thenflutterfire configure
in the root folder of the Flutter project - Add the following packages and the Firebase initialization script to
main.dart
(which is in thelib
folder).
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
// ..
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
With the changes, the main.dart
should look as follows.
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'firebase_options.dart';
import 'screens/note_screen.dart';
void main() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(ProviderScope(child: NotesApp()));
}
class NotesApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'My notes',
home: NoteScreen(),
);
}
}
With the changes in place, the application should still start up as before.
Creating a Firestore database
Next, we create a Firestore database. Access the Firebase console, open up our project, and select Firestore database. Click the "Create database" button to create a database for the project. Let's start with by creating a test database -- when creating a database, select "Start in Test mode". As the location, we use eur3
, which is a Multi-region location, but other locations work as well.
When a new Firestore database has been created, there are no collections yet. Create a new collection by clicking the "Start collection" button. We'll use notes
as the name -- leading to /notes
as the document path, and create a single note to the database. As the document id, we use the auto id option, and as the content, we use "hello world", as shown in Figure 1 below.

Now that we have a collection, let's access it from our application.
Accessing the database
To access the database, we need to have the cloud_firestore
plugin in the project, and we need to create the functionality for accessing the database.
Add the Firestore plugin (cloud_firestore
) to the project. Run the command flutter pub add cloud_firestore
, followed by the command flutterfire configure
. This will adjust the firebase_options.dart
to some and add the cloud_firestore
package to the pubspec.yaml
file. At this point, the dependencies
-part of pubspec.yaml
should be as follows.
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
flutter_riverpod: ^2.4.0
riverpod: ^2.4.0
uuid: ^4.0.0
firebase_core: ^2.9.0
cloud_firestore: ^4.5.1
Let's first test that we can access the database. Modify the main.dart
as follows and restart the application.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'firebase_options.dart';
import 'screens/note_screen.dart';
void main() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final snapshot = await _firestore.collection('notes').get();
snapshot.docs.forEach((doc) => print('${doc.id}: ${doc.data()}'));
runApp(ProviderScope(child: NotesApp()));
}
class NotesApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'My notes',
home: NoteScreen(),
);
}
}
Running the above code should, in addition to launching the application, lead to seeing the document content in the console.
vDlBixPz9654Kr58EWEZ: {content: hello world}
Now that we know that we can access the database, let's remove the changes related to logging the database contents from main.dart
and continue with adjusting the application to work with the database.
Notes and Firestore
Let's next adjust our application to retrieve the notes from Firestore. In the present state, the notes are stored in the NoteNotifier
and they are available only in the memory.
Note model
The data that we are working with is stored as maps in Firestore. Let's first adjust the Note
class to add convenience methods for transforming a map into a note and a note into a map. Adjust the Note
class to match the following.
class Note {
final String id;
final String content;
Note({required this.id, required this.content});
factory Note.fromFirestore(Map<String, dynamic> data, String id) {
return Note(
id: id,
content: data['content'],
);
}
Map<String, dynamic> toFirestore() {
return {
'content': content,
};
}
}
In brief, the Note.fromFirestore
method takes a map and a document id as parameters and returns a Note
instance (we'll explicitly use a separate document id as the Firebase APIs separate the id and the document contents). The Note.toFirestore
method returns a map that can be used to store the note in Firestore -- when storing a note, we'll use the key separately.
Note provider
Next, we need to adjust the NoteNotifier
to use the Note
class and to retrieve the notes from Firestore. Adjust the NoteNotifier
to match the following.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/note.dart';
class NoteNotifier extends StateNotifier<List<Note>> {
NoteNotifier() : super([]) {
_fetchNotes();
}
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
void _fetchNotes() async {
final snapshot = await _firestore.collection('notes').get();
final notes = snapshot.docs.map((doc) {
return Note.fromFirestore(doc.data(), doc.id);
}).toList();
state = notes;
}
void addNote(String content) async {
final noteData = Note(
id: '',
content: content,
).toFirestore();
final noteRef = await _firestore.collection('notes').add(noteData);
final note = Note.fromFirestore(noteData, noteRef.id);
state = [...state, note];
}
void deleteNote(String id) async {
await _firestore.collection('notes').doc(id).delete();
state = state.where((note) => note.id != id).toList();
}
}
final noteProvider =
StateNotifierProvider<NoteNotifier, List<Note>>((ref) => NoteNotifier());
Now, the NoteNotifier
retrieves the notes from Firestore when opening the application, and stores them in the state. The addNote
and deleteNote
methods have been adjusted to use the Firestore API. We've also dropped the uuid
package as we no longer need it -- now, we receive the note identifiers from Firestore.
To accommodate this, you can also drop the uuid
package from the pubspec.yaml
file, which should now be as follows.
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
flutter_riverpod: ^2.4.0
riverpod: ^2.4.0
firebase_core: ^2.9.0
cloud_firestore: ^4.5.1
Note that our above implementation is missing useful things such as error handling. In the case of errors, the user should likely be notified of them, but we'll omit such details for now.
Now, when you start the application, you can see that the notes are retrieved from Firestore and displayed in the application. Similarly, any changes to the notes are reflected in the application and in the database. The Figure 2 shows the application running with the "hello world" document retrieved from Firestore.
