Flutter Firebase Database example – Firebase Database CRUD with ListView

Flutter Firebase Database example – Firebase Database CRUD with ListView

Firebase Realtime Database is a cloud-hosted database that helps us to store and sync data with NoSQL cloud database in realtime to every connected client. In this tutorial, we’re gonna build a Flutter App that allows us to make CRUD interactions with Firebase Database in a ListView.

Related Posts:
How to integrate Firebase into Flutter App – Android Studio
Flutter Navigator example – Send/Return data to/from new Screen
Flutter ListView example with ListView.builder
Flutter Firestore example – Firebase Firestore CRUD with ListView

Flutter Firestore Example Overview

We will build a Flutter App that supports showing, inserting, editing, deleting Notes from/to Firebase Realtime Database with ListView:

flutter-firebase-database-example-overview

Firebase Console for Realtime Database will be like:

flutter-firebase-database-example-database-results

You can find how to:
– use Flutter ListView at: Flutter ListView example with ListView.builder
– send/receive data between screens at: Flutter Navigator example – Send/Return data to/from new Screen

Firebase Realtime Database

Configure Android App for Firebase project

Register and Integrate Firebase

We’ve already had a Tutorial, please visit: How to integrate Firebase into Flutter App – Android Studio.

Setup Firebase Database

Open Firebase Console, and select your project.

Under Develop, select Database:

integrate-flutter-firebase-example-configure-android-enter-database

In Realtime Database, set the Rules:

flutter-firebase-database-example-configure-database-rules

Update pubspec.yaml

Open pubspec.yaml in Flutter Project. Add a dependency for firebase_database:

dependencies:
  flutter:
    sdk: flutter
  firebase_database: 1.0.3

Run command: flutter packages get.

CRUD Operations

Initialize & Reference

To get main reference, we need to do is get access to static field in FirebaseDatabase class. If we wanna access more specific child of our database, just use child() method:

final notesReference = FirebaseDatabase.instance.reference();
final notesReference = FirebaseDatabase.instance.reference().child('notes');

Create

notesReference.push().set({
  'title': 'ozenero.com',
  'description': 'Programming Tutorials'
}).then((_) {
  // ...
});

Read

To get data from database, we add listener to the reference. When getting the value, we simply add it to list (state variable) and call setState() method.

class _ListViewNoteState extends State {
  List items;
  StreamSubscription _onNoteAddedSubscription;

  @override
  void initState() {
    super.initState();
    items = new List();
    _onNoteAddedSubscription = notesReference.onChildAdded.listen(_onNoteAdded);
  }

  @override
  void dispose() {
    _onNoteAddedSubscription.cancel();
    super.dispose();
  }

  void _onNoteAdded(Event event) {
    setState(() {
      items.add(new Note.fromSnapshot(event.snapshot));
    });
  }
}

Update

Calling child(key) will return a reference to object we want to edit, then use set(newValue) method.

notesReference.child('object-id').set({
  'title': 'new title',
  'description': 'new description'
}).then((_) {
  // ...
});

Our application won’t know the change. To stay updated, we have to add listener:

class _ListViewNoteState extends State {

  @override
  void initState() {
    // ...
    _onNoteChangedSubscription = notesReference.onChildChanged.listen(_onNoteUpdated);
  }

  @override
  void dispose() {
    _onNoteChangedSubscription.cancel();
    super.dispose();
  }

  void _onNoteUpdated(Event event) {
    var oldNoteValue = items.singleWhere((note) => note.id == event.snapshot.key);
    setState(() {
      items[items.indexOf(oldNoteValue)] = new Note.fromSnapshot(event.snapshot);
    });
  }
}

Delete

notesReference.child('object-id').remove().then((_) {
    // ...
  });
}

Practice

Set up Project

Follow the steps above to create, configure Firebase Project and Flutter Project.

Project Structure

flutter-firebase-database-example-project-structure

Data Model

lib/model/note.dart

import 'package:firebase_database/firebase_database.dart';

class Note {
  String _id;
  String _title;
  String _description;

  Note(this._id, this._title, this._description);

  Note.map(dynamic obj) {
    this._id = obj['id'];
    this._title = obj['title'];
    this._description = obj['description'];
  }

  String get id => _id;
  String get title => _title;
  String get description => _description;

  Note.fromSnapshot(DataSnapshot snapshot) {
    _id = snapshot.key;
    _title = snapshot.value['title'];
    _description = snapshot.value['description'];
  }
}

UI

List of Items Screen

lib/ui/listview_note.dart

import 'dart:async';
import 'package:firebase_database/firebase_database.dart';

import 'package:flutter/material.dart';

import 'package:flutter_firebase/model/note.dart';
import 'package:flutter_firebase/ui/note_screen.dart';

class ListViewNote extends StatefulWidget {
  @override
  _ListViewNoteState createState() => new _ListViewNoteState();
}

final notesReference = FirebaseDatabase.instance.reference().child('notes');

class _ListViewNoteState extends State {
  List items;
  StreamSubscription _onNoteAddedSubscription;
  StreamSubscription _onNoteChangedSubscription;

  @override
  void initState() {
    super.initState();

    items = new List();

    _onNoteAddedSubscription = notesReference.onChildAdded.listen(_onNoteAdded);
    _onNoteChangedSubscription = notesReference.onChildChanged.listen(_onNoteUpdated);
  }

  @override
  void dispose() {
    _onNoteAddedSubscription.cancel();
    _onNoteChangedSubscription.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ozenero Firebase DB Demo',
      home: Scaffold(
        appBar: AppBar(
          title: Text('ozenero Firebase DB Demo'),
          centerTitle: true,
          backgroundColor: Colors.blue,
        ),
        body: Center(
          child: ListView.builder(
              itemCount: items.length,
              padding: const EdgeInsets.all(15.0),
              itemBuilder: (context, position) {
                return Column(
                  children: [
                    Divider(height: 5.0),
                    ListTile(
                      title: Text(
                        '${items[position].title}',
                        style: TextStyle(
                          fontSize: 22.0,
                          color: Colors.deepOrangeAccent,
                        ),
                      ),
                      subtitle: Text(
                        '${items[position].description}',
                        style: new TextStyle(
                          fontSize: 18.0,
                          fontStyle: FontStyle.italic,
                        ),
                      ),
                      leading: Column(
                        children: [
                          Padding(padding: EdgeInsets.all(10.0)),
                          CircleAvatar(
                            backgroundColor: Colors.blueAccent,
                            radius: 15.0,
                            child: Text(
                              '${position + 1}',
                              style: TextStyle(
                                fontSize: 22.0,
                                color: Colors.white,
                              ),
                            ),
                          ),
                          IconButton(
                              icon: const Icon(Icons.remove_circle_outline),
                              onPressed: () => _deleteNote(context, items[position], position)),
                        ],
                      ),
                      onTap: () => _navigateToNote(context, items[position]),
                    ),
                  ],
                );
              }),
        ),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: () => _createNewNote(context),
        ),
      ),
    );
  }

  void _onNoteAdded(Event event) {
    setState(() {
      items.add(new Note.fromSnapshot(event.snapshot));
    });
  }

  void _onNoteUpdated(Event event) {
    var oldNoteValue = items.singleWhere((note) => note.id == event.snapshot.key);
    setState(() {
      items[items.indexOf(oldNoteValue)] = new Note.fromSnapshot(event.snapshot);
    });
  }

  void _deleteNote(BuildContext context, Note note, int position) async {
    await notesReference.child(note.id).remove().then((_) {
      setState(() {
        items.removeAt(position);
      });
    });
  }

  void _navigateToNote(BuildContext context, Note note) async {
    await Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => NoteScreen(note)),
    );
  }

  void _createNewNote(BuildContext context) async {
    await Navigator.push(
      context,
      MaterialPageRoute(builder: (context) => NoteScreen(Note(null, '', ''))),
    );
  }
}

Item Screen

lib/ui/note_screen.dart

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

import 'package:flutter_firebase/model/note.dart';

class NoteScreen extends StatefulWidget {
  final Note note;
  NoteScreen(this.note);

  @override
  State createState() => new _NoteScreenState();
}

final notesReference = FirebaseDatabase.instance.reference().child('notes');

class _NoteScreenState extends State {
  TextEditingController _titleController;
  TextEditingController _descriptionController;

  @override
  void initState() {
    super.initState();

    _titleController = new TextEditingController(text: widget.note.title);
    _descriptionController = new TextEditingController(text: widget.note.description);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Note')),
      body: Container(
        margin: EdgeInsets.all(15.0),
        alignment: Alignment.center,
        child: Column(
          children: [
            TextField(
              controller: _titleController,
              decoration: InputDecoration(labelText: 'Title'),
            ),
            Padding(padding: new EdgeInsets.all(5.0)),
            TextField(
              controller: _descriptionController,
              decoration: InputDecoration(labelText: 'Description'),
            ),
            Padding(padding: new EdgeInsets.all(5.0)),
            RaisedButton(
              child: (widget.note.id != null) ? Text('Update') : Text('Add'),
              onPressed: () {
                if (widget.note.id != null) {
                  notesReference.child(widget.note.id).set({
                    'title': _titleController.text,
                    'description': _descriptionController.text
                  }).then((_) {
                    Navigator.pop(context);
                  });
                } else {
                  notesReference.push().set({
                    'title': _titleController.text,
                    'description': _descriptionController.text
                  }).then((_) {
                    Navigator.pop(context);
                  });
                }
              },
            ),
          ],
        ),
      ),
    );
  }
}

Source Code

flutter_firebase_database

4 1 vote
Article Rating
Subscribe
Notify of
guest
5.8K Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments