Firebase FunctionsのPythonコードをFlutterから呼び出すためのメモ
Firebase FunctionsをPythonで書いて、Flutterアプリから呼び出す方法についてまとめました。
Functionsの作成
1. Firebaseプロジェクトを初期化
firebase init
2. 言語の選択肢
使用する言語を聞かれるので、Pythonを選択します。
? What language would you like to use to write Cloud Functions?
JavaScript
TypeScript
❯ Python
Pythonを選ぶと、Firebaseが自動的にPython用のファイルを生成してくれます。
main.py
を編集
3. まずはデフォルトで生成されるmain.py
のコメントを外して、以下のように編集します。
from firebase_functions import https_fn
from firebase_admin import initialize_app
initialize_app()
@https_fn.on_request()
def on_request_example(req: https_fn.Request) -> https_fn.Response:
return https_fn.Response("Hello world!")
4. Functionsのデプロイ
firebase deploy --only functions
onCallを使用したFunctionsの作成
クライアントからFirebase Authenticationで認証されたユーザーのみがCloud Functionsを呼び出せるようにしたいため、onCallを使います。
from firebase_admin import initialize_app
from firebase_functions import https_fn
from typing import Any
initialize_app()
@https_fn.on_call()
def on_call_example(req: https_fn.Request) -> Any:
# on_callを使用する場合はJSON形式で返す必要がある
return {"response":"hello"}
特定の関数のみをデプロイしたい場合は、以下のようにします。
firebase deploy --only functions:on_request_example
FlutterからFunctionsを呼び出す
FlutterからFunctionsを呼び出すために以下のパッケージを使います。
firebase_auth
cloud_functions
2. FlutterからFunctionsを呼び出す
import 'package:cloud_functions/cloud_functions.dart';
import 'package:firebase_auth/firebase_auth.dart';
Future<void> callTestFunction() async {
// 匿名認証
await FirebaseAuth.instance.signInAnonymously();
try {
// Firebase Functionsを呼び出す
final result = await FirebaseFunctions.instance.httpsCallable('on_call_example').call();
print('result ${result.data}');
} on FirebaseFunctionsException catch (e) {
print('Function error: ${e.code}, ${e.details}');
}
}
リージョンを指定したFunctionsの作成とデプロイ
デフォルトでは、Functionsはus-central1にデプロイされますが、リージョンを指定することで、他のリージョンにデプロイすることができます。リージョンを変更する場合はFunctions側とFlutter側の両方でリージョンを指定する必要があります。
1. Functions側でリージョンを指定
from firebase_admin import initialize_app
from firebase_functions import https_fn, options
from typing import Any
initialize_app()
# リージョンにasia-northeast1を指定
options.set_global_options(region=options.SupportedRegion.ASIA_NORTHEAST1)
@https_fn.on_call()
def on_call_example(req: https_fn.Request) -> Any:
return {"response":"hello"}
2. Functionsをデプロイ
先ほどと同様にデプロイすると、リージョンが指定されたFunctionsがデプロイされます。
firebaseのコンソールから確認すると、リージョンがasia-northeast1
に変更されていることが確認できます。
3. Flutter側でリージョンを指定してFunctionsを呼び出す
リージョンを指定した場合、Flutterから呼び出す際にもリージョンを指定する必要があります。
instanceForのregion引数に先ほど指定したasia-northeast1
を指定して呼び出します。
Future<void> callTestFunction() async {
await FirebaseAuth.instance.signInAnonymously();
try {
// Firebase Functionsをリージョン指定して呼び出す
final result = await FirebaseFunctions.instanceFor(region: 'asia-northeast1').httpsCallable('on_call_example').call();
print('result ${result.data}');
} on FirebaseFunctionsException catch (e) {
print('Function error: ${e.code}, ${e.details}');
}
}
Functionsエミュレーターのセットアップ
詳しい記事はこちら
1. エミュレーターを初期化
firebase init emulators
今回は既存のプロジェクトを使用
=== Project Setup
? Please select an option:
❯ Use an existing project
Create a new project
Add Firebase to an existing Google Cloud Platform project
Don't set up a default project
2. Functions Emulatorを選択
どのFirebase emulators機能を使用するか聞かれるので、Functions Emulatorを選択した状態でエンターキーを押します
=== Emulators Setup
? Which Firebase emulators do you want to set up? Press Space to select
emulators, then Enter to confirm your choices. (Press <space> to select, <a> to
toggle all, <i> to invert selection, and <enter> to proceed)
◯ Eventarc Emulator
◯ Authentication Emulator
❯◉ Functions Emulator
◯ Firestore Emulator
◯ Database Emulator
◯ Hosting Emulator
◯ Pub/Sub Emulator
(Move up and down to reveal more choices)
あとの設定はデフォルトで問題ないのでエンターキーを押します
=== Emulators Setup
? Which Firebase emulators do you want to set up? Press Space to select emulators, then Enter to confirm your choices. Functions Emulator
? Which port do you want to use for the functions emulator? 5001
? Would you like to enable the Emulator UI? Yes
? Which port do you want to use for the Emulator UI (leave empty to use any available port)?
? Would you like to download the emulators now? Yes
i ui: downloading ui-v1.11.7.zip...
i Writing configuration info to firebase.json...
i Writing project information to .firebaserc...
✔ Firebase initialization complete!
3. エミュレーターを起動
$ firebase emulators:start
実行すると、以下のようなメッセージが表示されます。
┌─────────────────────────────────────────────────────────────┐
│ ✔ All emulators ready! It is now safe to connect your app. │
│ i View Emulator UI at http://127.0.0.1:4000/ │
└─────────────────────────────────────────────────────────────┘
┌───────────┬────────────────┬─────────────────────────────────┐
│ Emulator │ Host:Port │ View in Emulator UI │
├───────────┼────────────────┼─────────────────────────────────┤
│ Functions │ 127.0.0.1:5001 │ http://127.0.0.1:4000/functions │
└───────────┴────────────────┴─────────────────────────────────┘
Emulator Hub running at 127.0.0.1:4400
Other reserved ports: 4500
Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
4. Pythonコードの編集
エミュレーターの確認用に、以下のようなコードを追加します。
@https_fn.on_call()
def on_call_emulator(req: https_fn.Request) -> Any:
return {"response":"hello from emulator"}
iOS版Flutterアプリからエミュレーターを呼び出す
useFunctionsEmulator
を使用してエミュレーターを指定します。
第一引数にlocalhost
、第二引数にエミュレーター起動時に表示されたポート番号を指定します。
final _functions = FirebaseFunctions.instanceFor(region: 'asia-northeast1');
Future<void> callTestFunction() async {
_functions.useFunctionsEmulator('localhost',5001);
await FirebaseAuth.instance.signInAnonymously();
try {
final result = await _functions.httpsCallable('on_call_emulator').call();
print('result ${result.data}');
} on FirebaseFunctionsException catch (e) {
print('Function error: ${e.code}, ${e.details}');
}
}
Android版Flutterアプリからエミュレーターを呼び出す
先ほどエミュレーターの実行をiOSで確認していましたが、Androidの場合は実行結果がUNAVAILABLE
となり通信がブロックされてしまいます。
これはエミュレータがHTTPSではなくHTTPで通信していて、Android版のFlutterアプリではデフォルトでHTTP通信がブロックされているためのようです。
その他めAndroid版では、以下の手順が必要です。
AndroidManifest.xml
でクリアテキストトラフィックを許可
1. <application
android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config">
2. ネットワークセキュリティ設定ファイルを作成
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain>10.0.2.2</domain>
</domain-config>
</network-security-config>
3. キャッシュをクリア
flutter clean
以上の手順により、Android版でもエミュレーターを使用してローカル環境のFunctionsを呼び出すことができます。
Discussion