概要
こちらは Flutter #2 Advent Calendar 2020 の 14日目の記事です。
まだ、本格的に開発で使っていないのですが、iOS/Androidのアプリ開発に加えてゆくゆくWebアプリも開発できる予定のマルチプラットフォーム Flutter をキャッチアップ中で調査したり触ったりしております。
先日、12/2に開催された PORT Firebase × Flutter オンライン に参加しました。
#PORTJP
— gremito #ものラジ QAスクラムでCSM® フリーランス (@grem_ito) December 2, 2020
React/Vueはhot reloadがあるのにFlutter Webはhot reloadが無いんですね汗
ありがとうございます!!!
そこで気になったことが、iOS/Androidのビジネスロジック( /lib
配下のDartクラス )とは分離してしまうことになるものの、Flutter Web に Firebase JavaScript SDK が組み込めて使えるのか調査してみることにしました。
前提
Flutter と Firebase の入門的な話(前提知識やスタートガイドなどの情報)は割愛いたしますmm
公式のドキュメントがしっかり解説されており、参考になる記事も増えてきているため、本記事以外を参考によろしくお願いします。
また Flutter Web は、公式が正式版をまだ公開しておらず、ベータ版で提供されています。
そのため、本記事は責任を一切負えないため、参考程度に使用してください。
Flutter と Firebase をどう管理するか
まずは、Building a web application with Flutter の内容を済ませておき、flutter create
でプロジェクト用意しておく。
その後にfirebase init
しますが、同じ階層でFirebaseも一緒に管理するとなると困難になり、挙動が変わる原因にもなりかねないため以下のようにディレクトリを分けてFlutterリソースとFirebaseリソースを管理すると良いでしょう。
Firebase JavaScript SDK
Firebaseコンソールのダッシュボードに表示されている↑Webアプリの追加を済ませて、設定画面からJavascript SDKの組み込み方法を確認する。
このまま Firebase Hosting で Flutter Web リソースをアップして対応まで考えると左の図で良いのですが、そもそも使えるのかわからないので、まずは右のCDN版で実行して使えることを確かめていきます。
Firebase SDK snippet
上記右画像のFirebase SDK snippet
に書かれている組み込み方法をそのまま書いて実行してみたところ次のようにエラーで正常動作しませんでした。
<!DOCTYPE html>
<html>
<head>
<base href="/">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="flutter_web_sample">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>flutter_web_sample</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('flutter-first-frame', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
<script src="main.dart.js" type="application/javascript"></script>
<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js"></script>
<script>
// Your web app's Firebase configuration
var firebaseConfig = {
apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "flutter-server-app.firebaseapp.com",
projectId: "flutter-server-app",
storageBucket: "flutter-server-app.appspot.com",
messagingSenderId: "01234567890",
appId: "0:01234567890987654321:web:abcd01234efghi56789"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
</script>
</body>
</html>
$ flutter run -d chrome
Running "flutter pub get" in flutter... 1,117ms
Launching lib/main.dart on Chrome in debug mode...
Syncing files to device Chrome... 37.3s
Debug service listening on ws://127.0.0.1:58489/XXXXXXXXXXXX=/ws
Warning: Flutter's support for web development is not stable yet and hasn't
been thoroughly tested in production environments.
For more information see https://flutter.dev/web
🔥 To hot restart changes while running, press "r" or "R".
For a more detailed help message, press "h". To quit, press "q".
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
The following JSNoSuchMethodError was thrown building MyApp(dirty):
TypeError: Cannot read property 'MaterialApp' of undefined
The relevant error-causing widget was:
MyApp file:///Users/TAKUYA_AIR/flutter-projects/flutter_web_sample/flutter/lib/main.dart:4:10
When the exception was thrown, this was the stack:
packages/flutter_web_sample/main.dart 11:12 build
packages/flutter/src/widgets/framework.dart 4749:28 build
packages/flutter/src/widgets/framework.dart 4675:15 performRebuild
packages/flutter/src/widgets/framework.dart 4369:5 rebuild
packages/flutter/src/widgets/framework.dart 4654:5 [_firstBuild]
packages/flutter/src/widgets/framework.dart 4649:5 mount
packages/flutter/src/widgets/framework.dart 3615:13 inflateWidget
packages/flutter/src/widgets/framework.dart 3380:18 updateChild
packages/flutter/src/widgets/binding.dart 1203:16 [_rebuild]
packages/flutter/src/widgets/binding.dart 1174:5 mount
packages/flutter/src/widgets/binding.dart 1116:16 <fn>
packages/flutter/src/widgets/framework.dart 2730:19 buildScope
packages/flutter/src/widgets/binding.dart 1115:12 attachToRenderTree
packages/flutter/src/widgets/binding.dart 954:24 attachRootWidget
packages/flutter/src/widgets/binding.dart 936:7 <fn>
dart-sdk/lib/_internal/js_dev_runtime/private/isolate_helper.dart 48:19 internalCallback
════════════════════════════════════════════════════════════════════════════════════════════════════
dartのエラーは、おそらくhtmlサイドでエラーになっているから連携できず影響しているんだと思って、flutter run
に出ているdartに関するエラーはあまり気にしないことにしました。
公式ドキュメント
次に JavaScript SDKのスタートガイドページの ステップ 3: Firebase SDK を追加して Firebase を初期化する を確認して以下の箇所を変えてみました。すると flutter run
にエラーは表示されず、やはり正常動作ではないものの結果が変わりました。
【省略】
<!-- The core Firebase JS SDK is always required and must be listed first -->
<!-- <script src="https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js"></script> -->
<script defer src="https://www.gstatic.com/firebasejs/7.23.0/firebase-app.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/7.23.0/firebase-auth.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/7.23.0/firebase-firestore.js"></script>
【省略】
firebase_core_web
調べていると [firebase_core_web] Uncaught ReferenceError: firebase is not defined #2826 というissueを見つけて、内容を確認すると本記事と同じく Flutter Web で Firebase を組み込んだ際の問題でした。
ただ、Closeされているものの何が原因だったのかわからずで、とりあえず思ったことを試していくことに。
以前、Flutter のセットアップの際にiOSだけエラーだったもののWebだけ開発するなら大丈夫だろうと無視していたが、結局 flutter doctor
が全て正常でなければビルドできない経験がありました。
もしかすると今回も似たような感じで、Webだけ対応していてiOS/AndroidはFirebaseの対応していないからエラーになっている可能性があるなと思い、firebase_core_web
を入れて確認してみるものの変化はありませんでした・・・笑汗
Web Installation | FlutterFire
firebase_core_web
をしっかり確認していくと、ここで Flutter と Firebase を連携する際に提供しているドキュメントがあることを改めて確認(存在は知っていました笑)し、JavaScript SDK の組み込み方法が解説されている Web Installation の存在がわかりましたw
どうやらflutter_service_worker.js
やmain.dart.js
の準備よりも前に Firebase を組み込むようでした。
案の定、正常動作したので試しに Firebase RemoteConfig と連携してみました。
【省略】
<!-- The core Firebase JS SDK is always required and must be listed first -->
<!-- <script src="https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js"></script> -->
<!-- <script defer src="https://www.gstatic.com/firebasejs/7.23.0/firebase-app.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/7.23.0/firebase-auth.js"></script>
<script defer src="https://www.gstatic.com/firebasejs/7.23.0/firebase-firestore.js"></script> -->
<script src="https://www.gstatic.com/firebasejs/7.23.0/firebase-app.js"></script>
<script>
// Your web app's Firebase configuration
var firebaseConfig = {
apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "flutter-server-app.firebaseapp.com",
projectId: "flutter-server-app",
storageBucket: "flutter-server-app.appspot.com",
messagingSenderId: "01234567890",
appId: "0:01234567890987654321:web:abcd01234efghi56789"
};
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
// Remote Config
const remoteConfig = firebase.remoteConfig();
remoteConfig.fetchAndActivate()
.then(() => {
const serviceName = remoteConfig.getValue('service_name');
console.log("service_name: " + serviceName._value);
})
.catch((err) => {
console.error(err);
});
</script>
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('flutter-first-frame', function () {
navigator.serviceWorker.register('flutter_service_worker.js');
});
}
</script>
<script src="main.dart.js" type="application/javascript"></script>
【省略】
Hosting
JavaScript SDK を調整して Hosting にアップして確認したところ問題なく動きました!
$ cd flutter
$ flutter build web
Running "flutter pub get" in flutter... 573ms
Compiling lib/main.dart for the Web... 29.2s
$ cd ../firebase
$ cp -r ../flutter/build/web/ public/
$ firebase deploy --only hosting
=== Deploying to 'flutter-server-app'...
i deploying hosting
i hosting[flutter-server-app]: beginning deploy...
i hosting[flutter-server-app]: found 14 files in public
✔ hosting[flutter-server-app]: file upload complete
i hosting[flutter-server-app]: finalizing version...
✔ hosting[flutter-server-app]: version finalized
i hosting[flutter-server-app]: releasing new version...
✔ hosting[flutter-server-app]: release complete
✔ Deploy complete!
Project Console: https://console.firebase.google.com/project/flutter-server-app/overview
Hosting URL: https://flutter-server-app.web.app
まとめ
Flutter に Firebase を導入される際は、 FlutterFire を参考にしましょうー!😇