Flutter Web で Firestore を使う


Flutter WebでFirestore動かせるのかなーと思ったのでやってみました。

事前準備

flutter webのアプリをfirebase hostingで公開するのときも書いたけど、flutter webを動かせるところまでは下記等を参考に

flutter_web
flutter webでexampleを動かすまで(flutterインストール済みを想定)
Flutter Webを動かしたときにつまづいたこと

flutter webが動かせる状態で、tools-support-for-flutter-web-developmentを参考に新規プロジェクトを作成

FirebaseのWeb版の設定を行う

dart packageのfirebaseのドキュメントに記載されているとおりに下記のように設定を行いました。

pubspec.yamlを更新

pubspec.yamlのdependenciesにfirebaseを追加。

dependencies:
  firebase: ^5.0.0

web/index.htmlを更新

web/index.htmlにfirebaseのscriptタグを追加。

  <script src="https://www.gstatic.com/firebasejs/5.5.2/firebase-app.js"></script>
  <script src="https://www.gstatic.com/firebasejs/5.5.2/firebase-firestore.js"></script>

main.dartにfirestoreの記述等を追加

flutterのプロジェクト作成時の雛形にfirestoreの諸々の(とりあえず動く)コードを追加(initializeAppの内容は変更が必要です)。

import 'package:firebase/firebase.dart';
import 'package:firebase/firestore.dart' as fs;
import 'package:flutter_web/material.dart';

void main() {
  initializeApp(
    apiKey: "YourApiKey",
    authDomain: "YourAuthDomain",
    databaseURL: "YourDatabaseUrl",
    projectId: "YourProjectId",
    storageBucket: "YourStorageBucket",
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final fs.Firestore store = firestore();
  final List<Map<String, dynamic>> messages = List();

  fetchMessages() async {
    var messagesRef = await store.collection('messages').get();

    messagesRef.forEach(
      (doc) {
        messages.add(doc.data());
      },
    );

    setState(() {});
  }

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

  @override
  Widget build(BuildContext context) {
    // The Flutter framework has been optimized to make rerunning build methods
    // fast, so that you can just rebuild anything that needs updating rather
    // than having to individually change instances of widgets.
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () async {
          var m = Map<String, String>();
          m['content'] = 'hogehoge';
          await store.collection('messages').add(m);
          setState(() {
            messages.add(m);
          });
        },
        child: Icon(Icons.add),
      ),
      body: ListView(
        children: messages.map(
          (message) {
            return ListTile(title: Text(message['content']));
          },
        ).toList(),
      ),
    );
  }
}

悲しいことに下記コマンドだとエラーはきます。
* この件のissue

$ webdev serve

なぜか下記のようにrelease modeだと動きます。
この問題が直るまでは遊びで使うのも厳しい感じがします。

$ webdev serve -r

下のように無事表示されました。(floating action button何度か押したあとのものです。)

Flutter Webはまだテクニカルプレビューですが、URLが変わらない問題+配信のサイズが大きい問題を除けば、すぐに遊びに使うには十分な領域に達しそうだなと思いました。
全然コード書けねーってなったのでDartもFlutterもちゃんと勉強しようとも思いました。。。

追記

このissueによれば

$ webdev serve

で動かすにはpubspec.yamlのdev_dependenciesのbuild_runnerとbuild_web_compilersを下記のように変更すれば良いようです。

dev_dependencies:
  build_runner: any
  build_web_compilers: '>=1.0.0 <2.0.0'

上記のようにすることでwebdev serve -rを使わなくとも動くようにはなるみたいです。

関連

前回はflutter webのアプリをfirebase hostingで公開する書いてました。

[Flutter] 既存のFlutterのコードをFlutter Webで動かしてみたってやつも書きました(Firebaseは関係ないやつです)。