flutter_hooks+firestoreで始める、掲示板アプリ
事前準備
- 前提条件
Firebaseプロジェクトがすでに作られている。
Firestoreのセキュリティモードがテスト、またはAuthができる状態である。 - 依存関係をインストール
dependencies:
flutter:
sdk: flutter
flutter_hooks: ^0.18.1
firebase_core: ^1.10.6
cloud_firestore: ^3.1.5
- flutterfire_cliのインストール
以下のコマンドを入力し、インストール
dart pub global activate flutterfire_cli
参照:https://firebase.flutter.dev/docs/overview/#initializing-flutterfire
- flutterアプリとfirebaseの連携
flutterアプリとfirebaseを連携します。
まず、目的のFlutterアプリプロジェクトのルートに移動し、以下のコマンドを入力します。
flutterfire configure
そうするとどのFirebaseプロジェクトを連携するか、どのプラットフォームで利用するかを聞かれます。
全ての質問に答えて上げましょう。
そうするとlibディレクトリに、firebase_options.dartができていると思います。
これは、Firebaseとのプラットフォームごとの連携をよしなにやってくれるdartファイルになります。
- firebase_coreを初期化
ここから、コーディングを初めます。
扱うパッケージを全てインポートします。
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'firebase_options.dart';
次に、firebase_coreの初期化を行います。
main関数を編集します。
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
- MyAppを作成
class MyApp extends HookWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Text("まだ、なにも"),
);
}
}
- BordPageを作成
class BoardPage extends HookWidget {
const BoardPage({Key? key}) : super(key: key);
void _bottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
builder: (_) {
return HookBuilder(
builder: (context) {
final name = useTextEditingController(text: '名無し');
final content = useTextEditingController();
return Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
TextField(
decoration: const InputDecoration(
labelText: '名前',
),
controller: name,
),
TextField(
decoration: const InputDecoration(
labelText: 'コメント',
),
controller: content,
),
ElevatedButton(
child: const Text('投稿'),
onPressed: () {
FirebaseFirestore.instance.collection('posts').add({
'name': name.text,
'content': content.text,
'time': Timestamp.now(),
});
Navigator.pop(context);
},
),
],
);
},
);
},
);
}
Widget build(BuildContext context) {
List<Widget> listTile = [];
final memo = useMemoized(() => FirebaseFirestore.instance
.collection('posts')
.orderBy("time")
.snapshots());
final snapshot = useStream(memo);
if (snapshot.hasData) {
final docs = snapshot.data?.docs;
docs?.forEach((element) {
listTile.add(ListTile(
title: Text(element.data()['content']),
subtitle: Text(element.data()['name']),
));
});
}
return Scaffold(
appBar: AppBar(
title: const Text('アントちゃんねるへようこそ'),
),
body: ListView(
children: listTile,
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_bottomSheet(context);
},
child: const Icon(Icons.add),
),
);
}
}
- MyAppを編集
class MyApp extends HookWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const BoardPage(),
);
}
}
- なにがおきているのか?
final memo = useMemoized(() => FirebaseFirestore.instance
.collection('posts')
.orderBy("time")
.snapshots());
final snapshot = useStream(memo);
useMemoizedでFirestoreのPostコレクションのキャッシュをとっています。これで、Firestoreに更新があったときだけ、firestoreのpostsコレクションを読み込むようになります。
また、orderBy("time")でドキュメントを投稿時間でソートしています。
useStreamでは、キャッシュされたスナップショットをstreamとして受け取ります。これで、StreamBuilderを使わず、Firestoreの保持しているデータを扱えるようになります。
if (snapshot.hasData) {
final docs = snapshot.data?.docs;
docs?.forEach((element) {
listTile.add(ListTile(
title: Text(element.data()['content']),
subtitle: Text(element.data()['name']),
));
});
}
これはsnapshotがデータを持って入れば、listTileというリストにListTileを追加していく処理になります。docsにはpostsコレクションのドキュメントが入っています。
final name = useTextEditingController(text: '名無し');
final content = useTextEditingController();
useTextEdittingControllerをつかうことでTextFieldの値をかんたんに扱うことができます。
onPressed: () {
FirebaseFirestore.instance.collection('posts').add({
'name': name.text,
'content': content.text,
'time': Timestamp.now(),
});
Navigator.pop(context);
},
ボタンが押されたさい、Firestoreのpostsコレクションにドキュメントを追加します。
timeには現在のTimestampを、nameには投稿者の名前を、contentには投稿内容を記述します。
- 終わりに
今回作った掲示板は、firebaseでホスティングしています。以下のURLから遊んでみてください。
https://tutorial-firestore-bfe2c.web.app/#/
Discussion