todo機能の作成
realtime databaseの作成


firebaseのrealtime databaseはデータをアプリケーションとリアルタイムで同期することが可能。
今回はtodoアプリのタスク情報を保存するのに使用する
今回はこちらの記事を参考に実装します。
Flutter Firebaseデータベースの例 – ListViewを使用したFirebaseデータベースのCRUD
タスクを作成・削除を行う機能を実装します。
flutter-todo/lib/ui/listview_note.dart
import 'dart:async'; import 'package:firebase_database/firebase_database.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/observer.dart'; import 'package:flutter/material.dart'; import 'package:flutter_sample/model/note.dart'; import 'package:flutter_sample/ui/note_screen.dart'; class ListViewNote extends StatefulWidget { ListViewNote({Key key, this.title, this.analytics, this.observer}) : super(key: key); final String title; final FirebaseAnalytics analytics; final FirebaseAnalyticsObserver observer; @override _ListViewNoteState createState() => new _ListViewNoteState(analytics, observer); } // notesのインスタンスを取得 final notesReference = FirebaseDatabase.instance.reference().child('notes'); class _ListViewNoteState extends State<ListViewNote> { _ListViewNoteState(this.analytics, this.observer); final FirebaseAnalyticsObserver observer; final FirebaseAnalytics analytics; List<Note> items; StreamSubscription<Event> _onNoteAddedSubscription; StreamSubscription<Event> _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: 'Todo List', home: Scaffold( appBar: AppBar( title: Text('Todo List'), centerTitle: true, ), body: _showListView(), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: () => _createNewNote(context), ), ), ); } Widget _showListView() { return Center( child: ListView.builder( itemCount: items.length, padding: const EdgeInsets.all(15.0), itemBuilder: (context, position) { return Column( children: <Widget>[ Divider(height: 5.0), Card( child: InkWell( child: Row( children: <Widget>[ _showTskNumber(position), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ _showTaskTitle(position), _showTaskDescription(position), ], ), ), _showDeleteButton(position), ], ), onTap: () => _navigateToNote(context, items[position]), ), ), ], ); }), ); } Widget _showTskNumber(int position) { return Padding( padding: EdgeInsets.symmetric(horizontal: 10.0), child: CircleAvatar( backgroundColor: Colors.blueAccent, radius: 10.0, child: Text( '${position + 1}', style: TextStyle( color: Colors.white, ), ), ), ); } Widget _showTaskTitle(int position) { return Padding( padding: EdgeInsets.only(left: 10), child: SizedBox( width: 300, child: Text( '${items[position].title}', style: TextStyle(fontWeight: FontWeight.bold), textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, maxLines: 2, ), ), ); } Widget _showTaskDescription(int position) { return Padding( padding: EdgeInsets.only(right: 0), child: SizedBox( width: 250, child: Text( '${items[position].description}', textAlign: TextAlign.center, overflow: TextOverflow.ellipsis, maxLines: 3, ), ), ); } Widget _showDeleteButton(int position) { return Padding( padding: EdgeInsets.symmetric(horizontal: 10.0), child: IconButton( icon: const Icon(Icons.remove_circle_outline), onPressed: () => _deleteNote(context, items[position], position) ), ); } 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, '', ''))), ); } }
flutter-todo/lib/ui/note_screen.dart
import 'package:flutter/material.dart'; import 'package:firebase_database/firebase_database.dart'; import 'package:flutter_sample/model/note.dart'; class NoteScreen extends StatefulWidget { final Note note; NoteScreen(this.note); @override State<StatefulWidget> createState() => new _NoteScreenState(); } final notesReference = FirebaseDatabase.instance.reference().child('notes'); class _NoteScreenState extends State<NoteScreen> { 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: <Widget>[ 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); }); } }, ), ], ), ), ); } }
flutter-todo/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']; } }
flutter-todo/lib/main.dart
import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_analytics/observer.dart'; import 'package:flutter/material.dart'; import 'package:flutter_sample/ui/login.dart'; import 'package:flutter_sample/ui/listview_note.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { static FirebaseAnalytics analytics = FirebaseAnalytics(); static FirebaseAnalyticsObserver observer = FirebaseAnalyticsObserver(analytics: analytics); @override Widget build(BuildContext context) { return MaterialApp( title: 'Todo List', navigatorObservers: <NavigatorObserver>[observer], home: ListViewNote(), ); } }

実際にタスクを登録してみましょう
firebaseでデータの状態がリアルタイムに確認することができます。
DBのセキュリティ設定
DBのセキュリティの設定についてはこちらで紹介しています。
DBのセキュリティ設定
