目次
ゲームデザイン部中村です。
引き続きFlutterによるアプリ開発について記載していきます。前回・前々回については以下のリンクからご覧ください。
Flutterの導入とウィジェットレイアウトについて説明してきましたが、SNSやショッピングサービスなどではアプリのみですべてのサービスを完結させることはできません。サーバ上にユーザ情報を格納し、SNSではサーバから友達の情報を取得しなければならず、ショッピングサービスでは商品の在庫があるかをサーバに問い合わせなければなりません。このようなとき、サーバ側の環境構築をすべきなのですが、非常に面倒です。
そこで今回は幅広くサーバ側にサービスを用意している、Flutterと非常に相性の良いFirebaseとの連携を行います。
今回は画像多めでお送りします。
Firebaseとは
Googleが提供するアプリケーション開発プラットフォームです。代表的な6つのサービスがあり、その特徴は「クロスプラットフォーム」「インフラ構築不要」ということにあります。
Authenticationはユーザ認証を手軽に導入できます。メール/パスワードだけでなく、Google・Twitter・Facebook・Appleなどのユーザ認証を一つのソースコードで行うことができます。
CloudFirestoreはスケーラブルなNoSQLデータベースです。MySQLやPostgreSQLとは異なり、データを保存するドキュメントがあり、コレクションの中に複数のドキュメントが存在します。
RealtimeDatabaseはJSON形式のデータを保存するNoSQLクラウドデータベースです。保存されたデータは接続しているすべてのクライアントで同期され、リアルタイムに更新されます。
Storageは写真や動画などを保存、提供するストレージサービスです。
HostingはWebコンテンツホスティングサービスです。Webアプリ、静的コンテンツ、動的コンテンツをCDNに配信することができます。
CloudFunctionsはサーバレスにHTTPSリクエストをトリガーとしてバックエンドコードを実行します。マネージド環境で実行されるため、個別のインフラ構築が不要です。
以上6つのサービスから、今回はCloudFirestoreにデータを格納しStorageに画像を保管するという流れを説明していきます。
Firebaseプロジェクトの作成
まずはFirebaseコンソールへ移動しましょう。
「プロジェクトを追加」ボタンから作成を開始します。
「まずプロジェクトに名前をつけましょう」と表示されるので今回は「mTest」と名付けました。「続行」します。
「このプロジェクトで Google アナリティクスを有効にする」のチェックがありますので、ONのままにして「続行」します。
「Google アナリティクスの構成」では「新しい構成」から名前をつけ、アナリティクスの地域を日本にしました。
以上でプロジェクトが作成されます。
iOS構成設定
プロジェクト画面が開いたら、iOSでFirebaseを利用するための構成を進めます。
「アプリにFirebaseを追加して利用開始しましょう」からiOSを選択します。
「アプリの登録」ではバンドルIDを登録します。今回は「com.sample.mtest」としました。その他の項目は空欄で「アプリを登録」。
「設定ファイルのダウンロード」が表示されますので、GoogleServices-Info.plistをダウンロードします。SDKの追加、初期化コードの追加は行いませんのでそのまま「次へ」と進み「コンソールに進む」で完了です。
CloudFirestoreの初期設定
Flutterで表示するための仮データを作成していきましょう。
まずは左側メニューからCloudFirestoreを選択し、「データベースの作成」をクリックしましょう。
データベースの作成では「テストモードで開始する」にしておきます。作成後にセキュリティルールを設定しない限り30日間ユーザ認証なしに誰でも参照できるようになるので、ここままサービスリリースできない点はご注意ください。
「CloudFirestoreのロケーション」はasia-northeast1にしておきます。テストのうちは特に気にする必要はありませんが、普段から同じロケーションに設定しておくと忘れなくて良いと思います。
しばらく待つとデータベースが作成されるので、「コレクションの追加」を行います。今回は仮データとして食べ物を5つ用意します。
必要なデータとしては「name」「price」「image」を用意します。imageには後で用意する食べ物画像のファイル名を指定します。
同じ要領でうどん、パスタ、カレーライス、ピザを用意しました。
Storageの初期設定
続いてStorageに食べ物の画像を設置します。こちらはすでにファイルストレージとして利用できるようになっていますので、「ファイルをアップロード」から5枚の食べ物画像をアップロードします。これらの画像はいらすとやさんからお借りしました。
また、今回はFirebaseAuthを利用したユーザ認証については省略しているため、Storageのルールを以下のように編集します。これによって、ユーザ認証なしでStorageのファイルを読み書きできます。
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write;
}
}
}
FlutterとFirebase連携
Firebaseでの仮データができたのでFlutterからFirebaseにアクセスするための設定を進めていきます。
FlutterにFirebaseSDKを導入
ウィジェットレイアウト編でも登場したpubspec.yaml
にプラグインを記述します。
dependencies:
flutter:
sdk: flutter
http: ^0.12.2
# 以下を追加
firebase_core: ^0.5.0
cloud_firestore: ^0.14.0
firebase_storage: ^5.2.0
pubspec.yaml
に追記をするとホットリロードが動作するので問題なくアプリが動くことを確認します。
続いて、FirebaseにバンドルIDをcom.sample.mtest
と登録したので、アプリ側のバンドルIDも変更します。FlutterアプリはVSCodeからプロジェクトを生成すると自動でcom.example.{プロジェクト名}
というバンドルIDに設定されます。そのため、VSCodeの文字列検索からこのバンドルIDを探し、全て差し替えます。
続いて、iOS構成設定でダウンロードしたGoogleServices-Info.plistをプロジェクト内に配置します。このファイルにはiOSアプリからFirebaseへアクセスするための認証情報が含まれています。その内容はFirebaseのプロジェクト毎に異なるので注意してください。
また、このファイルの追加にはXCodeが必要です。MacのFinderからプロジェクトを開き、ios/Runner.xcodeproj
をダブルクリックしてXCodeを起動しましょう。配置位置は以下の画像を参照してください。
FlutterにてFirebaseからデータを取得する
初期化コード
以上でFirebaseとの連携準備は完了しましたので、ソースコードの方を編集してFirebaseからデータを取得しましょう。
まずはFirebaseを利用するための初期化を行います。main.dartの上部でFirebaseをimportします。
~~~
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart' as cloud_firestore;
import 'package:firebase_storage/firebase_storage.dart' as firebase_storage;
これら3行でFirebaseのコア、Firestore、Storageをimportできます。FirestoreとStrageについてはas
オプションで別名利用できるようにしています。
さらに、もともとあったvoid main()
関数を以下のように書き換えます。
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
main関数をFutureにすることで、内部でawaitが使えるようになります。これによって、アプリの起動直前にFirebaseの初期化を行ない終了まで待つことができます。ただし、アプリが起動しないとFlutterで用意されたメソッドが呼び出せないため、WidgetsFlutterBinding.ensureInitialized();
でFlutterの機能を呼び出せるようにしています。これはおまじないと思っていただいて問題ありません。
ここまでの書き換えではまだFirebaseの機能を呼び出していないため、前回作成した天気のリスト表示が同じ用に表示されることを確認してください。
CloudFirestoreから食べ物リストを取得
続いて、CloudFirestoreからデータを取得してきましょう。Scaffold
を以下のように編集します。
Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: StreamBuilder<cloud_firestore.QuerySnapshot>(
stream: cloud_firestore.FirebaseFirestore.instance.collection('items').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return LinearProgressIndicator();
return ListView(
children: snapshot.data.docs.map[1]e)=>ListTile(
title: Text("${e.get('name')}"),
subtitle: Text("${e.get('price')}円"),
.toList(),
);
},
)
)
);
ここではStreamBuilder
が登場します。
ウィジェットレイアウト編ではFutureBuilder
を利用してHTTPレスポンスを画面に描画しました。FutureBuilder
は一度の呼び出し結果を待ち、レイアウトに反映します。
それに対してStreamBuilder
はstream
に指定したsnapshot
に変更がある度、自動でレイアウトに反映します。CloudFirestoreはこの機能を利用してデータの変更を検知しレイアウトを更新できるようになっています。
上記Scaffold
の書き換えによって以下のようなレイアウトができたと思います。
それでは実際にStreamBuilder
の効果を確認しましょう。ブラウザのCloudFirestoreとiOSシミュレータを画面上に並べて表示し、食べ物名や金額を編集してみましょう。ブラウザでデータを編集すると即座にiOSシミュレータに反映されることが確認できます。
Storageから画像を取得
CloudFirestoreに用意した食べ物について、それぞれのアイコンを表示するためにStorageから画像を取得しましょう。
アイコンのファイル名をCloudFirestore内のデータにimage
として指定してあるため、このファイル名でStorageに画像の問い合わせを行います。まずはHTTPリクエストで画像を表示するために以下のプラグインを追加します。
dependencies:
~~~~
cached_network_image: ^2.4.1
また、このプラグインをmain.dartにimport
します。
import 'package:cached_network_image/cached_network_image.dart';
また、main.dartの最下部に以下のクラスを追加します。このクラスではFuture<String>
を指定して、そこで受け取る文字列を画像のURLとしてCachedNetworkImage
に渡すFutureBuilder
として機能します。
~~~~
class NetworkImageBuilder extends FutureBuilder {
NetworkImageBuilder(Future<String> item)
: item = item,
super(
future: item,
builder: (context, snapshot) {
if(snapshot.hasData) {
return CachedNetworkImage(
imageUrl: snapshot.data,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
);
} else {
return CircularProgressIndicator();
}
},
);
final Future<String> item;
}
上記クラスをListTile
内で以下のように利用します。
ListTile(
leading: NetworkImageBuilder(firebase_storage.FirebaseStorage.instance.ref().child(e.get('image')).getDownloadURL()),
title: Text("${e.get('name')}"),
subtitle: Text("${e.get('price')}円"),
)
ここでStorageから画像を取得するURLをNetworkImageBuilder
に指定しています。firebase_storage.FirebaseStorage.instance.ref()
でStorageにアクセスするリファレンスを取得し、child(e.get('image'))
で該当のファイルを指定、getDownloadURL()
でファイルにアクセスするURLを取得しています。このときgetDownloadURL()
はFuture<String>
を返却するため、一旦FutureBuilder
でURLを待ち、その後CachedNetworkImage
で画像の取得を待つ、という流れが必要となります。
以上の編集によって以下のようなリストが確認できると思います。これでStorageから画像を取得することができました。
以上、三回に渡ってFlutterによるアプリ開発を記述してきました。
導入編ではFlutterのSDKを用意しアプリ画面が表示されるまで説明しました。
ウィジェットレイアウト編ではアプリらしいレイアウトを作るための方法と、HTTPを通してサーバからデータを取得する方法を説明しました。
そして今回はFlutterと相性の良いFirebaseとの連携について、FirestoreとStorageを用いて説明しました。FirebaseについてはAuthenticationを導入してユーザ認証の実装をおすすめします。
ここまで記述してきた情報によって様々なアプリ開発が可能かと思います。是非アプリ開発を行ってみてください。
References
↑1 | e)=>ListTile( title: Text("${e.get('name')}"), subtitle: Text("${e.get('price')}円"), |
---|