Flutter Firebase Functions の利用(カスタムビジネスロジックの実装)

Flutter

はじめに

Flutterアプリケーションでカスタムビジネスロジックを実装する場合、バックエンドとしてFirebase Functionsを使用することが非常に有効です。Firebase Functionsは、クラウド上でバックエンドロジックを実行できるサーバーレスなソリューションで、特定のイベントに応じた処理や、複雑な計算、API連携などを行うことができます。

本記事では、FlutterアプリケーションとFirebase Functionsを連携し、カスタムビジネスロジックを実装するための基本的な手順を解説します。エンジニア初心者でも理解しやすいように、環境設定から実装、テストまでを順を追って説明します。

⚠️ 注意: Functionsを使うにはFirebaseのプランを従量課金制にする必要があります。


Firebase Functions とは?

Firebase Functionsは、特定のイベント(HTTPリクエストやFirebaseのデータ変更)をトリガーとして、カスタムロジックを実行できるクラウドベースのサービスです。サーバーの設定や管理を行う必要がなく、特定のバックエンド処理をクラウドで効率的に処理できます。

Firebase Functions の主な特徴

  • サーバーレス:サーバー管理が不要で、スケーラブルな処理を自動的に実行。
  • イベント駆動:FirestoreやAuthentication、Storageのイベントをトリガーに処理を実行。
  • HTTPリクエスト:HTTPリクエストをトリガーにカスタムAPIのように利用可能。

環境構築

Firebase プロジェクトの作成

まずは、Firebaseコンソールで新しいプロジェクトを作成します。すでにFirebaseプロジェクトがある場合は、次のステップに進んでください。

Firebaseプロジェクトの作成・プロジェクトの初期化はこちらの記事で説明しています。
Firebase Authenticationのセットアップの手順後に続きを実施してください
(Firebase Authenticationを有効にする必要はありません)

Flutter Firebase Authentication の導入【初心者向け】
Firebase Authenticationを使ってFlutterアプリにGoogleサインインやメール認証を導入する方法を解説します。

Firebase functionsの設定

FlutterプロジェクトディレクトリでFirebase functionsの設定を行います。

Bash
firebase init functions

初期化中に、次のようなオプションを選択します:

  • Functionsの言語:JavaScriptまたはTypeScriptを選択(今回はJavaScriptで進めます)。
  • ESLint:コードの静的解析を行いたい場合は「はい」。

これで、functions/ディレクトリが生成され、Firebase Functionsを使用できるようになります。


Firebase Functions の実装

基本的なHTTP関数の実装

Firebase Functionsでは、HTTPリクエストをトリガーにする関数を簡単に実装できます。以下のコードは、HTTPリクエストに対して「Hello World」を返すシンプルな関数です。

JavaScript
const functions = require('firebase-functions');

exports.helloWorld = functions.https.onRequest((request, response) => {
  response.send("Hello from Firebase Functions!");
});

カスタムビジネスロジックの実装

次に、カスタムビジネスロジックを実装します。例えば、Flutterアプリから送信されたデータを処理し、その結果を返すAPIを作成します。

以下の例では、JSON形式のデータを受け取り、特定のビジネスロジックを適用して結果を返す関数を実装しています。

JavaScript
exports.calculateDiscount = functions.https.onRequest((req, res) => {
  const data = req.body;
  
  // データに基づいてカスタムロジックを実行
  const discount = data.price * 0.1;  // 10%割引を計算
  
  // 結果をJSONで返す
  res.json({
    originalPrice: data.price,
    discountPrice: data.price - discount
  });
});

デプロイ

関数の実装が完了したら、Firebaseにデプロイします。

Bash
cd functions
npm run deploy

デプロイが完了すると、指定した関数が利用可能になります。関数のURLはFirebaseのコンソールやデプロイログで確認できます。

もし失敗する場合、firebase.jsonのにbuildコマンドとlintコマンドの実行設定がされている場合があるので今回は削除します。

JSON
{
  "functions": [
    {
      "source": "functions",
      "codebase": "default",
      "ignore": [
        "node_modules",
        ".git",
        "firebase-debug.log",
        "firebase-debug.*.log",
        "*.local"
      ],
      // ここを修正
      "predeploy": []
    }
  ]
}

Flutter との連携

HTTPパッケージのインストール

FlutterアプリからFirebase Functionsを呼び出すためには、httpパッケージを使用します。pubspec.yamlに以下を追加し、パッケージをインストールします。

YAML
dependencies:
  http: ^0.13.3
Bash
flutter pub get

Firebase Functions の呼び出し

FlutterアプリからFirebase Functionsのカスタムロジックを呼び出し、データを送信してその結果を取得するコードを実装します。

Dart
import 'package:http/http.dart' as http;
import 'dart:convert';

class FirebaseService {
  Future<void> getDiscountPrice(double price) async {
    final url = Uri.parse('cloudfunctionsのトリガー欄に記載のurl');
    final response = await http.post(
      url,
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({'price': price}),
    );

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      print('Original Price: ${data['originalPrice']}');
      print('Discount Price: ${data['discountPrice']}');
    } else {
      throw Exception('Failed to get discount price');
    }
  }
}

UIに連携

FlutterのUIで、価格を入力して割引価格を計算する簡単な画面を作成します。

Dart
import 'package:flutter/material.dart';
import 'firebase_service.dart';

class DiscountScreen extends StatefulWidget {
  @override
  _DiscountScreenState createState() => _DiscountScreenState();
}

class _DiscountScreenState extends State<DiscountScreen> {
  final FirebaseService _firebaseService = FirebaseService();
  final TextEditingController _priceController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Calculate Discount')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: _priceController,
              decoration: InputDecoration(labelText: 'Enter price'),
              keyboardType: TextInputType.number,
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                final price = double.parse(_priceController.text);
                await _firebaseService.getDiscountPrice(price);
              },
              child: Text('Get Discount'),
            ),
          ],
        ),
      ),
    );
  }
}

今回のコードの全貌

functions/index.js

JavaScript
const functions = require('firebase-functions');

exports.calculateDiscount = functions.https.onRequest((req, res) => {
    const data = req.body;
    
    // データに基づいてカスタムロジックを実行
    const discount = data.price * 0.1;  // 10%割引を計算
    
    // 結果をJSONで返す
    res.json({
      originalPrice: data.price,
      discountPrice: data.price - discount
    });
  });

lib/main.dart

Dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

void main() async {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: DiscountScreen(),
    );
  }
}

class DiscountScreen extends StatefulWidget {
  @override
  _DiscountScreenState createState() => _DiscountScreenState();
}

class _DiscountScreenState extends State<DiscountScreen> {
  final FirebaseService _firebaseService = FirebaseService();
  final TextEditingController _priceController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Calculate Discount')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: _priceController,
              decoration: InputDecoration(labelText: 'Enter price'),
              keyboardType: TextInputType.number,
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                final price = double.parse(_priceController.text);
                await _firebaseService.getDiscountPrice(price);
              },
              child: Text('Get Discount'),
            ),
          ],
        ),
      ),
    );
  }
}

class FirebaseService {
  Future<void> getDiscountPrice(double price) async {
    print(price);
    // final url = Uri.parse('https://flutter-app-b5408.cloudfunctions.net/calculateDiscount');
    final url = Uri.parse('https://calculatediscount-d6nzowqehq-uc.a.run.app');
    final response = await http.post(
      url,
      headers: {'Content-Type': 'application/json'},
      body: jsonEncode({'price': price}),
    );

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      print('Original Price: ${data['originalPrice']}');
      print('Discount Price: ${data['discountPrice']}');
    } else {
      throw Exception('Failed to get discount price');
    }
  }
}

実行結果

コンソールで下記が表示されていたら成功


まとめ

この記事では、FlutterアプリケーションでFirebase Functionsを使い、カスタムビジネスロジックを実装する手順を解説しました。Firebase Functionsは、サーバーレスで簡単にバックエンドの処理を実装できるため、アプリの拡張性や柔軟性が向上します。

この記事のポイント:

  • Firebase Functionsを使ったカスタムビジネスロジックの実装
  • FlutterアプリとFirebase Functionsの連携方法
  • カスタムAPIの構築とデプロイ

Firebase FunctionsとFlutterを組み合わせることで、より複雑でパワフルなアプリケーションを構築できるようになります。今後は、さらに高度なロジックやデータ処理を取り入れて、アプリを発展させましょう。

コメント

タイトルとURLをコピーしました