Bird
Raised Fist0
Fluttermobile~20 mins

Realtime Database in Flutter - Mini App: Build & Ship

Choose your learning style10 modes available
Build: Realtime Todo List
A simple todo list app screen that syncs tasks in real-time using Firebase Realtime Database. Users can add new tasks, see the list update instantly, and remove tasks.
Target UI
-------------------------
| Realtime Todo List     |
|-----------------------|
| + Add Task             |
|-----------------------|
| [ ] Buy groceries      |
| [ ] Walk the dog       |
| [ ] Call mom           |
|-----------------------|
Connect to Firebase Realtime Database
Display a list of todo tasks fetched in real-time
Add new tasks with a TextField and a button
Remove tasks by tapping on them
Update the UI instantly when database changes
Starter Code
Flutter
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/firebase_database.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Realtime Todo List',
      home: const TodoScreen(),
    );
  }
}

class TodoScreen extends StatefulWidget {
  const TodoScreen({super.key});

  @override
  State<TodoScreen> createState() => _TodoScreenState();
}

class _TodoScreenState extends State<TodoScreen> {
  final DatabaseReference _todoRef = FirebaseDatabase.instance.ref('todos');
  final TextEditingController _controller = TextEditingController();

  // TODO: Add code to listen to realtime updates and display list
  // TODO: Add code to add new todo
  // TODO: Add code to remove todo

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Realtime Todo List')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      labelText: 'New Task',
                    ),
                  ),
                ),
                IconButton(
                  icon: const Icon(Icons.add),
                  onPressed: () {
                    // TODO: Add new task to database
                  },
                ),
              ],
            ),
          ),
          Expanded(
            child: Center(
              child: Text('Loading tasks...'),
            ),
          ),
        ],
      ),
    );
  }
}
Task 1
Task 2
Task 3
Task 4
Solution
Flutter
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_database/firebase_database.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Realtime Todo List',
      home: const TodoScreen(),
    );
  }
}

class TodoScreen extends StatefulWidget {
  const TodoScreen({super.key});

  @override
  State<TodoScreen> createState() => _TodoScreenState();
}

class _TodoScreenState extends State<TodoScreen> {
  final DatabaseReference _todoRef = FirebaseDatabase.instance.ref('todos');
  final TextEditingController _controller = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Realtime Todo List')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(8.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      labelText: 'New Task',
                    ),
                  ),
                ),
                IconButton(
                  icon: const Icon(Icons.add),
                  onPressed: () {
                    final text = _controller.text.trim();
                    if (text.isNotEmpty) {
                      _todoRef.push().set({'task': text});
                      _controller.clear();
                    }
                  },
                ),
              ],
            ),
          ),
          Expanded(
            child: StreamBuilder<DatabaseEvent>(
              stream: _todoRef.onValue,
              builder: (context, snapshot) {
                if (snapshot.hasData && snapshot.data!.snapshot.value != null) {
                  final data = Map<String, dynamic>.from(snapshot.data!.snapshot.value as Map);
                  final tasks = data.entries.toList();
                  return ListView.builder(
                    itemCount: tasks.length,
                    itemBuilder: (context, index) {
                      final key = tasks[index].key;
                      final task = tasks[index].value['task'] as String;
                      return ListTile(
                        title: Text(task),
                        onTap: () {
                          _todoRef.child(key).remove();
                        },
                      );
                    },
                  );
                } else if (snapshot.hasError) {
                  return Center(child: Text('Error loading tasks'));
                } else {
                  return const Center(child: CircularProgressIndicator());
                }
              },
            ),
          ),
        ],
      ),
    );
  }
}

This app connects to Firebase Realtime Database and listens to the 'todos' node using onValue stream. When data changes, the UI updates instantly showing the current list of tasks.

Users can add new tasks by typing in the TextField and pressing the '+' button. This pushes a new child with the task text to the database.

Tapping a task removes it from the database, which also updates the UI in real-time.

This shows how easy it is to build a live-updating app with Firebase Realtime Database and Flutter.

Final Result
Completed Screen
-------------------------
| Realtime Todo List     |
|-----------------------|
| + Add Task             |
|-----------------------|
| Buy groceries          |
| Walk the dog           |
| Call mom               |
|-----------------------|
User types a task in the text field and taps '+' to add it to the list instantly.
The list updates live when tasks are added or removed by any user.
Tapping a task removes it from the list and database immediately.
Stretch Goal
Add a confirmation dialog before deleting a task.
💡 Hint
Use showDialog with AlertDialog to ask user to confirm before calling remove() on the task.

Practice

(1/5)
1. What is the main purpose of Firebase Realtime Database in a Flutter app?
easy
A. To compile Flutter code into native apps
B. To store images locally on the device
C. To handle user interface animations
D. To keep app data synced instantly across devices

Solution

  1. Step 1: Understand Realtime Database role

    Firebase Realtime Database is designed to sync data instantly between clients and server.
  2. Step 2: Compare options with purpose

    Only To keep app data synced instantly across devices describes syncing data instantly, which matches the database's main use.
  3. Final Answer:

    To keep app data synced instantly across devices -> Option D
  4. Quick Check:

    Realtime Database = instant data sync [OK]
Hint: Realtime Database syncs data live, not UI or local files [OK]
Common Mistakes:
  • Confusing database with UI or local storage
  • Thinking it compiles code
  • Assuming it stores images locally
2. Which of the following is the correct way to get a reference to a Firebase Realtime Database node named 'users' in Flutter?
easy
A. FirebaseDatabase.getReference('users')
B. FirebaseDatabase.instance.ref('users')
C. FirebaseDatabase.instance.child('users')
D. FirebaseDatabase.ref('users')

Solution

  1. Step 1: Recall correct method to get database reference

    The official Flutter Firebase API uses FirebaseDatabase.instance.ref(path) to get a reference.
  2. Step 2: Check each option syntax

    FirebaseDatabase.instance.ref('users') uses the correct instance and method. Others are incorrect or outdated.
  3. Final Answer:

    FirebaseDatabase.instance.ref('users') -> Option B
  4. Quick Check:

    Use instance.ref('path') for database reference [OK]
Hint: Use FirebaseDatabase.instance.ref('node') to get reference [OK]
Common Mistakes:
  • Omitting .instance before .ref()
  • Using .child() directly on FirebaseDatabase
  • Using non-existent getReference() method
3. Given the following Flutter code snippet, what will be printed when the database value changes to {'name': 'Alice'}?
final ref = FirebaseDatabase.instance.ref('profile');
ref.onValue.listen((event) {
  final data = event.snapshot.value as Map?;
  print(data?['name']);
});
medium
A. Error: type cast failed
B. null
C. Alice
D. profile

Solution

  1. Step 1: Understand onValue listener behavior

    The listener triggers on data change and provides a snapshot with the new value.
  2. Step 2: Analyze code inside listener

    The snapshot value is cast to a Map and then accesses the 'name' key, which is 'Alice'.
  3. Final Answer:

    Alice -> Option C
  4. Quick Check:

    onValue prints updated 'name' = Alice [OK]
Hint: onValue gives snapshot; access data keys safely [OK]
Common Mistakes:
  • Ignoring null safety and casting
  • Expecting entire snapshot instead of value
  • Confusing key names or data types
4. Identify the error in this Flutter code that listens to Realtime Database changes:
final ref = FirebaseDatabase.instance.ref('messages');
ref.onValue.listen((event) {
  final data = event.snapshot.value as Map;
  print(data['text']);
});
medium
A. Casting snapshot.value to Map may cause runtime error
B. Missing await keyword before listen
C. ref.onValue should be ref.value
D. print statement syntax is incorrect

Solution

  1. Step 1: Check type casting of snapshot.value

    snapshot.value can be null or Map, so casting directly to Map risks runtime error.
  2. Step 2: Verify other code parts

    listen is a Stream subscription, no await needed; onValue is correct; print syntax is valid.
  3. Final Answer:

    Casting snapshot.value to Map may cause runtime error -> Option A
  4. Quick Check:

    Unsafe cast causes runtime error [OK]
Hint: Use Map or null check before casting [OK]
Common Mistakes:
  • Casting without null safety
  • Misusing async/await with listen
  • Confusing onValue with value property
5. You want to display a live list of chat messages from Firebase Realtime Database in Flutter. Which approach correctly listens and updates the UI when data changes?
hard
A. Use a StreamBuilder with FirebaseDatabase.instance.ref('chats').onValue and update UI from snapshot
B. Call FirebaseDatabase.instance.ref('chats').get() once and build UI with that data
C. Use a FutureBuilder with FirebaseDatabase.instance.ref('chats').once() to get data
D. Use setState inside a timer to fetch data every 5 seconds

Solution

  1. Step 1: Understand live data update needs

    To keep UI updated live, use a stream that listens to data changes.
  2. Step 2: Match Flutter widget to stream

    StreamBuilder listens to onValue stream and rebuilds UI on changes, perfect for live updates.
  3. Step 3: Evaluate other options

    get() and once() fetch data once, not live; timer with setState is inefficient and not real-time.
  4. Final Answer:

    Use a StreamBuilder with FirebaseDatabase.instance.ref('chats').onValue and update UI from snapshot -> Option A
  5. Quick Check:

    StreamBuilder + onValue = live UI updates [OK]
Hint: Use StreamBuilder with onValue for live database updates [OK]
Common Mistakes:
  • Using get() or once() for live data
  • Polling with timers instead of streams
  • Using FutureBuilder for continuous updates
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