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
:
Firebase Console for Realtime Database will be like:
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:
In Realtime Database, set the 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
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 StatecreateState() => 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); }); } }, ), ], ), ), ); } }