Flutterから利用できるFirebaseサービスをカウンターアプリで実践(Cloud Functions編)
はじめに
Flutterから利用できるFirebaseの機能をカウンターアプリに実装し、内容をまとめたシリーズの第12回目です。
今回はトリガーされたイベントに応じて、バックエンドコードを自動的に実行できるCloud Functions for Firebaseについてです。
Cloud Functions for Firebaseはjavascript
とtypescript
での記述に対応しており、サーバの管理やスケーリングせずにバックエンドを実装できるサービスです。
目次
シリーズの内容
回数 | 内容 | リンク |
---|---|---|
第0回 | 準備編 | ブログ |
第1回 | Analytics |
ブログ |
第2回 | Firebase Crashlytics |
ブログ |
第3回 | Firebase Performance Monitoring |
ブログ |
第4回 | Firebase Remote Config |
ブログ |
第5回 | Firebase Authentication |
ブログ |
第6回 | Cloud Firestore |
ブログ |
第7回 | Firebase Realtime Database |
ブログ |
第8回 | Cloud Storage for Firebase |
ブログ |
第9回 | Firebase Cloud Messaging |
ブログ |
第10回 | Firebase In-App Messaging |
ブログ |
第11回 | Firebase ML |
ブログ |
第12回 | Cloud Functions for Firebase |
イマココ |
第13回 | Firebase Hosting |
ブログ |
第14回 | Firebaseのその他のサービス | ブログ |
開発環境
項目 | 内容 |
---|---|
PC | Macbook Air(M1) |
Flutter | 3.0.1 |
Firebase | 11.0.1 |
FlutterFire | 0.2.2+2 |
デバッグデバイス | Android 12(APIレベル31) , Chrome |
準備
準備編が完了出来ているものとします。
Cloud FunctionsではNode.js環境が必要なので、まだ導入していない場合はnvmを使ってインストールします。
また、Cloud FunctionsはBlazeプランでのみ使用できるので、料金プランをアップデートする必要があります。 なお、ある一定回数までは無料枠が適用されます。
導入方法
npm
が使えるようなったら、firebase-tools
をインストールします。
npm install -g firebase-tools
プロジェクトを初期化します。
firebase login
が出来ている環境では、functions
とその他必要なツールを初期化します。
なお、今回は言語にJavaScript
を選択しました。
firebase init functions
初期化出来たら、プロジェクト内に新しくfunctions
フォルダが作成されました。
Cloud Functions
を実行するためのスクリプトを、functions/index.js
に記述していきます。
必要なモジュールをインポートします。
const functions = require("firebase-functions");
関数の定義です。 関数を呼び出すにはアプリから直接呼び出す方法と、HTTPリクエスト経由で関数を呼び出す方法、スケジュール設定で呼び出す方法があります。
今回のカウントアップ機能は、アプリから直接呼び出して使用するため、バックエンド側ではonCall
トリガーを使用します。
ついでに、UIDを呼び出せるかも実験してみます。
exports.functionsTest = functions.https.onCall((data, context) => { /// 数値読み取り const firstNumber = data.firstNumber; const secondNumber = data.secondNumber; /// 計算実行 const addNumber = firstNumber + secondNumber; /// ついでに、UIDも呼び出せるか実験 const contextUid = context.auth.uid; return { addNumber:addNumber, contextUid:contextUid } });
無限ループになっていないかなどを確認するため、デプロイする前にローカルエミュレータでテストします。 なお、App Checkを使用している場合は、エミュレーターが実行できず、App Checkのデバッグプロバイダーを使用する必要がありますが、現時点では公式サイトにも記載している通り、Dart APIが提供されていないので、各実行環境でデバッグプロバイダーを使用しなければなりません。
エミュレータの起動にはJava
が必要なので、Open JDKをインストールしておきます。
ローカルエミュレータをインストール&初期化し、必要に応じてfirebase init ***
で各プラグインをインストールします。
firebase init emulators
ローカルエミュレータを使うときは、Flutter側のmain
関数にuseFunctionsEmulatorの設定をします。
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // Ideal time to initialize FirebaseFunctions.instance.useFunctionsEmulator('localhost', 5001); ...
インストールできたらemulators:start
でエミュレータを起動し、ブラウザでhttp://localhost:4000/
(デフォルトの場合)を開きます。
firebase emulators:start
問題なければ、本番環境にデプロイします。
Firebase ConsoleからFunctions
に移動し、「開始」を選択します。
firebase deploy --only functions:functionsTest
Cloud FunctionsをFlutterプロジェクトに適用するため、pubspec.yaml
にcloud_functionsを導入します。
dependencies: cloud_functions: ^3.2.15
functions
を実行するコードを書きます。
import 'package:cloud_functions/cloud_functions.dart'; /// Cloud Functionsの実行 void addNumber() async { try { /// 数を final result = await FirebaseFunctions.instance .httpsCallable('functionsTest') .call({'firstNumber': _number, 'secondNumber': 1}); _number = result.data['addNumber']; print(result.data['contextUid']); } on FirebaseFunctionsException catch (error) { print(error.code); print(error.details); print(error.message); } }
カウントアップするためだけにCloud Functionsを使う贅沢なアプリが完成しました。
なお、参考までに、私の場合、App Checkを使わないとエラーでfunctionsが実行出来ませんでした。
App Checkは一度開始すると解除できないため検証のしようがなく、公式サイトには推奨としか記載されていなかったので、あくまで参考程度で記載しておきます。
コード全文
前回からの変更点です。
Cloud Functions for Firebase
を実装cloud_functions
ページを追加- その他、コードの変更
cloud_functions_page.dart
/// Flutter import 'package:flutter/material.dart'; /// Firebase import 'package:cloud_functions/cloud_functions.dart'; class CloudFunctionsPage extends StatefulWidget { const CloudFunctionsPage({Key? key}) : super(key: key); @override CloudFunctionsPageState createState() => CloudFunctionsPageState(); } class CloudFunctionsPageState extends State<CloudFunctionsPage> { /// 初期化 int _number = 0; /// Cloud Functionsの実行 void addNumber() async { try { /// 数を final result = await FirebaseFunctions.instance .httpsCallable('functionsTest') .call({'firstNumber': _number, 'secondNumber': 1}); _number = result.data['addNumber']; print(result.data['contextUid']); } on FirebaseFunctionsException catch (error) { print(error.code); print(error.details); print(error.message); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Cloud Functionsページ'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('You have pushed the button this many times:'), Text( '$_number', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () { setState(() { addNumber(); }); }, tooltip: 'Increment', child: const Icon(Icons.add), ), ); } }
functions/index.js
const functions = require("firebase-functions"); exports.functionsTest = functions.https.onCall(async(data, context) => { /// App Checkの実施 if (context.app == undefined) { throw new functions.https.HttpsError( 'failed-precondition', 'The function must be called from an App Check verified app.') } /// 数値読み取り const firstNumber = data.firstNumber; const secondNumber = data.secondNumber; /// 計算実行 const addNumber = firstNumber + secondNumber; /// ついでに、UIDも呼び出せるか実験 const contextUid = context.auth.uid; return { addNumber:addNumber, contextUid:contextUid } });
GitHubのページを貼ります。