🕑

FlutterでCloud Functions使ってみた!

2022/07/18に公開

入門レベルですが難しいです〜😇

エラーにハマったので、メモを残しておこうと思います。
Androidでのみ実行してます。

以前Node.jsで作成した、プロジェクトを使用します。
https://zenn.dev/joo_hashi/articles/d0691d4b7ae578

Cloud Functionsで使用するコード

今回は、必要なところだけでいいのでちょっと修正
index.js

// The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
const functions = require('firebase-functions');

// The Firebase Admin SDK to access Firestore.
const admin = require('firebase-admin');
admin.initializeApp();

exports.saveTimeStamp = functions.https.onRequest(async (req, res) => {
  // Grab the text parameter.
  const unixtime = req.query.unixtime; // 数字で取れる
  const unixtimeStr = new Date(unixtime * 1000); // ms秒(1000倍)

  // Push the new message into Firestore using the Firebase Admin SDK.
  const writeResult = await admin.firestore().collection('timestamp').add({unixtime: unixtime});
  // Send back a message that we've successfully written the message
  res.json({result: `Save TimeStamp with ID: ${writeResult.id} added. unixtime: ${unixtimeStr.toLocaleString('ja-JP', { timeZone: 'JST' })}`});
});

// http://localhost:5001/functions-test-f11ed/us-central1/saveTimeStamp?unixtime=1658024647

Flutterのプロジェクトを作成

flutter create http_post

HTTP通信をするのに、必要なパッケージを追加する

pubspec.yaml

name: http_post
description: A new Flutter project.

# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
  sdk: ">=2.17.3 <3.0.0"

# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2
  http: ^0.13.4

dev_dependencies:
  flutter_test:
    sdk: flutter

  # The "flutter_lints" package below contains a set of recommended lints to
  # encourage good coding practices. The lint set provided by the package is
  # activated in the `analysis_options.yaml` file located at the root of your
  # package. See that file for information about deactivating specific lint
  # rules and activating additional ones.
  flutter_lints: ^2.0.0

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter packages.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  # assets:
  #   - images/a_dot_burr.jpeg
  #   - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages

アプリ側のコードを書く
main.dart

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

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

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

  final String title;

  
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  DateTime _dateTime = DateTime.now();

  void _selectDate() async {
    var pickedDate = await showDatePicker(context: context, initialDate: _dateTime, firstDate: DateTime(2022), lastDate: DateTime.now().add(Duration(days:365)));
    if (pickedDate != null) {
      setState(() => _dateTime = DateTime(pickedDate.year, pickedDate.month, pickedDate.day, _dateTime.hour, _dateTime.minute));
    }
  }

  void _selectTime() async {
    var pickedTime = await showTimePicker(context: context, initialTime: TimeOfDay.fromDateTime(_dateTime));
    if (pickedTime != null) {
      setState(() {
        _dateTime = DateTime(_dateTime.year, _dateTime.month, _dateTime.day, pickedTime.hour, pickedTime.minute);
      },);
    }
  }

  void _sendTime(int timestamp) async {
    // https://localhost:5001/jboyexample/us-central1/saveTimeStamp?unixtime=1657986508
    var url = Uri.http('localhost:5001', '/dev-functions-679de/us-central1/saveTimeStamp', {'unixtime': '$timestamp'});
    var resp = await http.get(url);
    print('http.get($url) status code=${resp.statusCode}.');
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'DateTime:${_dateTime.toLocal().toString()}',
              style: Theme.of(context).textTheme.headline4,
            ),
            ElevatedButton(
              child: Text('set Date'),
              onPressed: _selectDate,
            ),
            ElevatedButton(
              child: Text('set Time'),
              onPressed: _selectTime,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          _sendTime(_dateTime.toUtc().millisecondsSinceEpoch ~/ 1000);
        },
        tooltip: 'send to Firestore via Cloud Functions',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

これで、完成といいたいが罠があった!

Flutterのプロジェクト内で、ターミナルでこのコマンドを入力するのですが、反応しなかった🤔

Android専用のコマンド?

adb reverse tcp:5001 tcp:5001
zsh: command not found: adb

ん、できないぞ😅
調べてみると、ミンプロの人が解説をしていた!
https://minpro.net/setting-android-debug-bridge

ああ〜そうかパスが通ってないから、このエラー出るんだよな😇
ホームディレクトリーの.zhshrcを開いて、このパスを追加しましょう!

export PATH=$PATH:/Users/自分の名前/Library/Android/sdk/platform-tools

私の場合だと

export PATH=$PATH:/Users/hashimotojunichi/Library/Android/sdk/platform-tools

準備できたので、やってみましょう!

まずは、バックエンドのNode.js側のサーバーを起動する。

firebase emulators:start

で次は、Flutter側のHTTPのURLの中に、ターミナルに表示されているNode.jsのプロジェクト名だけ、貼り付けて、Androidでbuildする。
今回だと

http://localhost:5001/dev-functions-679de/us-central1/saveTimeStamp」の、dev-functions-679de

を使います。
番号なんて付けてたか?

Flutter側の関数に設定をする

void _sendTime(int timestamp) async {
    var url = Uri.http('localhost:5001', '/dev-functions-679de/us-central1/saveTimeStamp', {'unixtime': '$timestamp'});
    var resp = await http.get(url);
    print('http.get($url) status code=${resp.statusCode}.');
  }

Flutterアプリで、buildされている状態で、先ほどのコマンドをターミナルで実行する。

adb reverse tcp:5001 tcp:5001

実行すると何も起きてないけど、一応機能している?

時間を設定してボタンを押してみる

おっ成功したみたいですね😇
では、もう一度押してみよう!

追加できたみたいですね。

やってみた感想

今回も入門レベルです。自分の実現したい機能を作るとなると、もっとCloud Functionsの知識が必要になるでしょうね。

学習の手順としては

  1. モダンJavaScriptの文法を学ぶ
  2. Webの知識、HTTP通信のPOST、GET、PUT、DELETEが何かぐらい勉強しておく
  3. Node.jsでバックエンドを作る
  4. Flutterでフロントエンドを作る

これができれば、学習コストは下げることができると思われます。新しい技術を覚えるとモチベーションが上がるので、嬉しいですね。
私、プログラミングの勉強を始めた頃は、PHPという言語を勉強していたのですが、つまらなくて飽きてしまいました😅

その後、JavaScritpやって、Rubyやったり、Docker、Laravel、React、Vue.jsたくさん勉強しましたが、SwiftUI->Flutter->Storybord->Flutterで落ち着きました😅

Flutterは素晴らしい技術です。地方の企業も新規の開発で採用しており、人気があり今後も成長することは、間違いないでしょう🧑‍💻

しかし、Flutter以外の技術も業務では必要になるので、学習時間を確保して勉強しないといけないですね。
エンジニアのお仕事に限ったことではないですが、新しい技術や知識を学ばない人は、転職活動では不利になるし市場価値も低くなるでしょうね😱

そうならないために、ノーコードでもクラウドでもいいので、世の中の企業が求めている技術が使える人材になることを目指しましょう🏄‍♂️

Discussion