Bird
Raised Fist0
Fluttermobile~20 mins

Cloud Firestore CRUD in Flutter - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Simple Firestore Todo
A screen to add, read, update, and delete todo items stored in Cloud Firestore.
Target UI
Simple Firestore Todo

+-----------------------------+
| Add Todo: [__________] [+]  |
+-----------------------------+
| Todos:                      |
| 1. Buy groceries   [Edit][X]|
| 2. Walk the dog    [Edit][X]|
|                             |
+-----------------------------+
TextField to enter a new todo item
Add button to save the new todo to Firestore
List of todos fetched live from Firestore
Each todo shows text with Edit and Delete buttons
Edit button allows changing the todo text inline
Delete button removes the todo from Firestore
Starter Code
Flutter
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

class SimpleFirestoreTodo extends StatefulWidget {
  @override
  State<SimpleFirestoreTodo> createState() => _SimpleFirestoreTodoState();
}

class _SimpleFirestoreTodoState extends State<SimpleFirestoreTodo> {
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Simple Firestore Todo')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: InputDecoration(hintText: 'Add Todo'),
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.add),
                  onPressed: () {
                    // TODO: Add new todo to Firestore
                  },
                ),
              ],
            ),
            Expanded(
              child: StreamBuilder<QuerySnapshot>(
                stream: FirebaseFirestore.instance.collection('todos').snapshots(),
                builder: (context, snapshot) {
                  if (!snapshot.hasData) return CircularProgressIndicator();
                  final docs = snapshot.data!.docs;
                  return ListView.builder(
                    itemCount: docs.length,
                    itemBuilder: (context, index) {
                      final doc = docs[index];
                      final todoText = doc['text'];
                      return ListTile(
                        title: Text(todoText),
                        trailing: Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            IconButton(
                              icon: Icon(Icons.edit),
                              onPressed: () {
                                // TODO: Edit todo text
                              },
                            ),
                            IconButton(
                              icon: Icon(Icons.delete),
                              onPressed: () {
                                // TODO: Delete todo
                              },
                            ),
                          ],
                        ),
                      );
                    },
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}
Task 1
Task 2
Task 3
Solution
Flutter
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

class SimpleFirestoreTodo extends StatefulWidget {
  @override
  State<SimpleFirestoreTodo> createState() => _SimpleFirestoreTodoState();
}

class _SimpleFirestoreTodoState extends State<SimpleFirestoreTodo> {
  final TextEditingController _controller = TextEditingController();

  void _addTodo() async {
    final text = _controller.text.trim();
    if (text.isEmpty) return;
    await FirebaseFirestore.instance.collection('todos').add({'text': text});
    _controller.clear();
  }

  void _deleteTodo(String id) async {
    await FirebaseFirestore.instance.collection('todos').doc(id).delete();
  }

  void _editTodoDialog(String id, String currentText) {
    final editController = TextEditingController(text: currentText);
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Edit Todo'),
        content: TextField(
          controller: editController,
          autofocus: true,
          decoration: InputDecoration(hintText: 'Todo text'),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: Text('Cancel'),
          ),
          TextButton(
            onPressed: () async {
              final newText = editController.text.trim();
              if (newText.isNotEmpty) {
                await FirebaseFirestore.instance.collection('todos').doc(id).update({'text': newText});
              }
              Navigator.pop(context);
            },
            child: Text('Save'),
          ),
        ],
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Simple Firestore Todo')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: InputDecoration(hintText: 'Add Todo'),
                    onSubmitted: (_) => _addTodo(),
                  ),
                ),
                IconButton(
                  icon: Icon(Icons.add),
                  onPressed: _addTodo,
                  tooltip: 'Add todo',
                ),
              ],
            ),
            Expanded(
              child: StreamBuilder<QuerySnapshot>(
                stream: FirebaseFirestore.instance.collection('todos').snapshots(),
                builder: (context, snapshot) {
                  if (snapshot.connectionState == ConnectionState.waiting) {
                    return Center(child: CircularProgressIndicator());
                  }
                  if (!snapshot.hasData || snapshot.data!.docs.isEmpty) {
                    return Center(child: Text('No todos yet'));
                  }
                  final docs = snapshot.data!.docs;
                  return ListView.builder(
                    itemCount: docs.length,
                    itemBuilder: (context, index) {
                      final doc = docs[index];
                      final todoText = doc['text'] ?? '';
                      return ListTile(
                        title: Text(todoText),
                        trailing: Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            IconButton(
                              icon: Icon(Icons.edit),
                              tooltip: 'Edit todo',
                              onPressed: () => _editTodoDialog(doc.id, todoText),
                            ),
                            IconButton(
                              icon: Icon(Icons.delete),
                              tooltip: 'Delete todo',
                              onPressed: () => _deleteTodo(doc.id),
                            ),
                          ],
                        ),
                      );
                    },
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}

This app uses Cloud Firestore to store todo items in a collection called 'todos'.

The _addTodo method adds a new document with the todo text. It clears the input after adding.

The _deleteTodo method deletes a document by its ID.

The _editTodoDialog shows a dialog with a TextField to update the todo text. On save, it updates the Firestore document.

The UI listens to live updates from Firestore using StreamBuilder and shows the list of todos. Each todo has Edit and Delete buttons.

This approach keeps the UI in sync with the database automatically.

Final Result
Completed Screen
Simple Firestore Todo

+-----------------------------+
| Add Todo: [Buy milk  ] [+] |
+-----------------------------+
| Todos:                      |
| 1. Buy groceries   [Edit][X]|
| 2. Walk the dog    [Edit][X]|
| 3. Buy milk        [Edit][X]|
+-----------------------------+
User types a todo in the text field and taps '+' to add it to Firestore.
The new todo appears instantly in the list below.
Tapping 'Edit' opens a dialog to change the todo text and save it.
Tapping 'X' deletes the todo from Firestore and removes it from the list.
Stretch Goal
Add a pull-to-refresh gesture to reload the todo list manually.
💡 Hint
Wrap the ListView with a RefreshIndicator widget and call setState or reload the Firestore stream on refresh.

Practice

(1/5)
1.

What does CRUD stand for in Cloud Firestore operations?

easy
A. Connect, Refresh, Upload, Delete
B. Copy, Run, Undo, Download
C. Create, Read, Update, Delete
D. Create, Remove, Update, Download

Solution

  1. Step 1: Understand CRUD acronym meaning

    CRUD is a common term in databases meaning the four basic operations on data.
  2. Step 2: Match each letter to the correct operation

    C = Create, R = Read, U = Update, D = Delete are the standard meanings.
  3. Final Answer:

    Create, Read, Update, Delete -> Option C
  4. Quick Check:

    CRUD = Create, Read, Update, Delete [OK]
Hint: Remember CRUD as basic data actions: create, read, update, delete [OK]
Common Mistakes:
  • Confusing CRUD letters with unrelated words
  • Mixing up 'Remove' with 'Delete'
  • Thinking CRUD includes 'Download'
2.

Which Flutter code snippet correctly adds a new document to a Firestore collection named users?

FirebaseFirestore.instance.collection('users').____({'name': 'Alice'});
easy
A. update
B. add
C. delete
D. get

Solution

  1. Step 1: Identify method to add new document

    The add method creates a new document with given data in the collection.
  2. Step 2: Confirm other methods' purposes

    update modifies existing documents, delete removes documents, get reads data.
  3. Final Answer:

    add -> Option B
  4. Quick Check:

    Adding new document uses add() [OK]
Hint: Use add() to create new documents in a collection [OK]
Common Mistakes:
  • Using update() to add new documents
  • Confusing get() with add()
  • Trying to delete() when adding data
3.

What will be the output of this Flutter Firestore code snippet?

var doc = await FirebaseFirestore.instance.collection('tasks').doc('task1').get();
print(doc.exists);

Assuming the document task1 exists in the tasks collection.

medium
A. true
B. false
C. null
D. Throws an error

Solution

  1. Step 1: Understand doc.get() behavior

    The get() method fetches the document snapshot from Firestore.
  2. Step 2: Check the exists property

    If the document exists, doc.exists returns true; otherwise false.
  3. Final Answer:

    true -> Option A
  4. Quick Check:

    Document exists property = true [OK]
Hint: doc.exists is true if document is found in Firestore [OK]
Common Mistakes:
  • Assuming doc.exists returns document data
  • Expecting false even if document exists
  • Not awaiting the get() call
4.

Identify the error in this Flutter Firestore update code:

FirebaseFirestore.instance.collection('users').doc('user1').update({'age': 30});

Assuming the document user1 does not exist.

medium
A. No error, update creates document if missing
B. Update silently fails without error
C. Syntax error in update method call
D. Throws an error because document does not exist

Solution

  1. Step 1: Understand update() behavior on missing document

    The update() method requires the document to exist; otherwise, it throws an error.
  2. Step 2: Confirm no syntax errors

    The syntax is correct, so error is due to missing document.
  3. Final Answer:

    Throws an error because document does not exist -> Option D
  4. Quick Check:

    update() fails if document missing [OK]
Hint: update() needs existing document; else error occurs [OK]
Common Mistakes:
  • Thinking update() creates new documents
  • Confusing update() with set()
  • Ignoring async/await for update()
5.

You want to delete a Firestore document only if it exists to avoid errors. Which Flutter code snippet correctly does this?

var docRef = FirebaseFirestore.instance.collection('orders').doc('order1');
// What code to safely delete?
hard
A. docRef.get().then((doc) { if (doc.exists) docRef.delete(); });
B. docRef.delete();
C. docRef.update({'deleted': true});
D. docRef.set(null);

Solution

  1. Step 1: Check document existence before deleting

    Use get() to fetch the document snapshot and check exists.
  2. Step 2: Call delete() only if document exists

    This avoids errors from deleting non-existing documents.
  3. Final Answer:

    docRef.get().then((doc) { if (doc.exists) docRef.delete(); }); -> Option A
  4. Quick Check:

    Check exists before delete to avoid errors [OK]
Hint: Always check doc.exists before delete() to prevent errors [OK]
Common Mistakes:
  • Calling delete() without checking existence
  • Using update() to delete documents
  • Setting document to null instead of deleting
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard