プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

Mac の VSCode で Flutter 用の設定

こちらの記事で、 mac 上の Flutter の開発環境として VSCode のセットアップをした話を書きました。

その際、 VSCode でプロジェクトの新規作成をすすると、すべてのプラットフォーム用のコードが作られるというのがありました。

なので、この設定を見直すため VSCode の設定ファイルを作成して、ある程度望みのものが作られるようにしたいと思います。その際のメモです。

settings.json

VSCode を立ち上げて、『設定』を開きます。『拡張機能』->『Dart』->『Flutter』と進みます。

こんな感じで、設定オプションがいくつもあるのがわかります。

これをスクロールしていくと、

Flutter Create Platforms という項目があるのがわかります。ここの説明に指定がない場合は、全てのプラットフォームを対象とする、ということが書いてありますね。

ここで、この『項目の追加』ボタンを押します。入力欄が表示されるので、 android と iOS を(1行に1プラットフォームで)書いておきます。

こんな感じですね。

ちなみに今回はユーザー用の設定ファイルに保存しました。

保存場所

なお、設定ファイルの保存場所は、 mac の場合は、

Library/Application Support/Code/User

に settings.json ファイルが作られます。ちなみに、この中身を見ると、

{
    "dart.flutterCreatePlatforms": [
        "android",
        "ios"
    ]
}

となってました。

テスト

上記の設定後、 Flutter の New Project を実行すると、

android と iOS のみのプロジェクトになってました。これで、不要なものは作らなくて良さそうです。

まとめ

AndroidStudio の場合だと、プロジェクトの作成時に、組織名(Java のパッケージ名として使われるやつ)も指定しているのですが、これはプロジェクトごとに変化するので、 VSCode の設定には馴染まなそうです。

まあ、新規プロジェクト作成後に、手作業で手直しになるのかな?この辺りは、どうするのがわかりやすいか、しばらく手探りになりそうです。

Mac 上の Flutter 開発環境を整える

今までは、 WIndows 上の Android Studio を利用して Flutter を開発して、ある程度動作するようになったら、 mac の XCode でビルドと実機デバッグを行うという方法を取っていました。

この方法でも、コードを修正することがほとんどないので、特に問題ありませんでした(修正が必要な場合も、大体簡単な手直しなのでコンソールで vim とかで対応しています)。

とはいえ、色々と触ってきて、 iOS 用の機能を調整する機会も増えてくるようになると、 mac 上にも開発環境が欲しくなってきます。

それに、こういう形はあんまり推奨されていないようで、下記などでは、けちょんけちょんに言われています。

Any Way To Use Xcode For Flutter Instead Of Android Studio Or VSCode - Stack Overflow

ということで、改めて、 mac に VSCode の Flutter 開発環境を整えたので、やったことを簡単にメモっておきます。

準備

何をすれば良いのかを確かめるため、下記の公式ドキュメントをざっと見ておきます。

Make iOS apps | Flutter

あと、現在の状態を確認しておきます。

mor@Junichi-MORI-Mac-mini ~ % flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[] Flutter (Channel stable, 3.22.2, on macOS 14.5 23F79 darwin-x64, locale ja-JP)
[] Android toolchain - develop for Android devices
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/docs/get-started/install/macos#android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, please use
      `flutter config --android-sdk` to update to that location.

[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
[] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[!] Android Studio (not installed)
[] Connected device (2 available)
[] Network resources

! Doctor found issues in 3 categories.
mor@Junichi-MORI-Mac-mini ~ % 

ということで、 Flutter の SDK も入ってるし、 VSCode 周りを設定するだけで良さそうですね。

VSCode のインストール

mac 版の VSCode ってマイクロソフトのサイトからダウンロードする必要があるんですね。

ということで、ダウンロードします。ダウンロードできたら、 Applications フォルダに移動させておきます。

VSCode のアイコンをダブルクリックすると VSCode が立ち上がります。立ち上がったら、拡張機能として

  • Japanese Language Pack for VS Code
  • Flutter

をインストールします。 Flutter をインストールすると Dart 拡張機能も自動的にインストールされます。

ここまでできたら、一度 VSCode を終了して再起動しておきます。

開発環境の確認

一応、Flutter doctor を使って確認しておきます。

mor@Junichi-MORI-Mac-mini ~ % flutter doctor   
Doctor summary (to see all details, run flutter doctor -v):
[] Flutter (Channel stable, 3.22.2, on macOS 14.5 23F79 darwin-x64, locale ja-JP)
[] Android toolchain - develop for Android devices
    ✗ Unable to locate Android SDK.
      Install Android Studio from: https://developer.android.com/studio/index.html
      On first launch it will assist you in installing the Android SDK components.
      (or visit https://flutter.dev/docs/get-started/install/macos#android-setup for detailed instructions).
      If the Android SDK has been installed to a custom location, please use
      `flutter config --android-sdk` to update to that location.

[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
[] Chrome - develop for the web (Cannot find Chrome executable at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome)
    ! Cannot find Chrome. Try setting CHROME_EXECUTABLE to a Chrome executable.
[!] Android Studio (not installed)
[] VS Code (version 1.94.2)
[] Connected device (2 available)
[] Network resources

! Doctor found issues in 3 categories.
mor@Junichi-MORI-Mac-mini ~ % 

VSCode に関する記述が増えただけですね。特に問題もなさそうです。

サンプルプロジェクトの作成と実機デバッグ

一応、開発環境が整ったということで、下記を参考に実際にサンプロプロジェクトを作成して、実行してみたいと思います。

【Dart】 Flutter × VSCode で、環境構築 から HelloWorld まで(Mac) | SIOS Tech. Lab

まず、 VSCode のコマンドパレットを立ち上げて、 Flutter と入力すると、

上記のような選択肢が表示されるので、 New Project を選択します。

次に、どのような Project を作成するかテンプレートの選択が表示されるので、

Application を選択します。

次に、どこにプロジェクトフォルダを作成するのか聞かれるので、

こんな感じに適当なフォルダを指定してやリます。

最後に、プロジェクト名を聞かれるので、

適当な名前をつけておきます。すると、先ほどのフォルダ配下に、プロジェクト名のフォルダが作成され、その内部に、flutterのプロジェクトが作られました。 VSCode 側でもこんな感じに見えます。

毎度お馴染みのカウンターアプリですね。

ところで、ここでプロジェクトフォルダをよくみてみると、ちょっと違和感があります。

フォルダに web や macon や linux などというのが見えます。

これ、今まで AndroidStudio で作成したときは、どのプラットフォームを対象にプロジェクトを作るかダイアログで選択してたんですが、考えてみると、 VSCode でプロジェクトを新規作成したときはそんな選択画面ありませんでした。

多分、そのため web, macos, linux などという具合に Flutter で扱える全てのプラットフォーム用のフォルダが作成されてしまっているようです。

この辺りは、ちょっと設定を変更してやる方が良さそうですね。

実行

では、作成したプロジェクトを実行してみたいと思います。実行するには、エミュレータか実機が必要になります。すでにエミュレータは作成済みなので、それを選択したいと思います。

VSCode の画面の下の方に、デバイス名が表示されている箇所があります。 No Device となっている場合もあるのですが、このときは macOS となってました。

ここをクリックすると、デバイスの選択メニューが表示されます。

ここで、『Start iOS Simulator』を選択してやります。

しばらくすると、上記のように iOS のエミュレータが起動しました。これで、デバイスの準備ができました。

ここまでできたら、サイドメニューから『実行とデバッグ』を選択します。

特に問題なければ、エミュレータにカウンターアプリが表示されます。

デバッグも VSCode でブレークポイントを設定して、呼び出してやれば、問題なく動作しました。

デバッグ設定とかを残しておきたいときは、 launch.json を作ってやればいいですね。

まとめ

思ったより簡単に開発環境を整えることができました。まあ、 XCode でビルドとかして、実機デバッグしてたので、そりゃやること少ないのも当然かな。

いずれにしても、これで mac でも開発作業がやりやすくなりそうです。

Firebase Remote Config を試しました

こちらの記事に書いたように、 Flutter プロジェクトで Firebase を使う準備ができたので、早速 Remote Config を試してみたので、そのメモです。

アプリ側の処理

まずは公式の説明に従って、進めてみたいと思います。

Firebase Remote Config を使ってみる

手順通り、 Remote Config を使うのに必要なライブラリを pubspec.yaml に追加します。

  • firebase_remote_config
  • firebase_analytics

ここまでは問題なし。

続いて、 RemoteConfig オブジェクトを取得するあたりからが、コードの一部しかないため、ちょっと全体がみえないです。

なので、ググって見つけた下記の記事および、それが参照している Youtube を参考にして進めてみます。

RemoteConfig を使うための実装

Firebase をセットアップした際の、プロジェクトの lib/main.dart にある _MyHomePageState クラスを修正してきます。

RemoteConfig のシングルトンは、このクラスのメンバー変数として取得します。

class _MyHomePageState extends State<MyHomePage> {
  final _remoteConfig = FirebaseRemoteConfig.instance;
  bool isLoading = false;

また、 RemoteConfig の設定処理中であることをあらわす変数 isLoading を追加しておきます。

RemoteConfig に関する設定処理は initState内で行います。

  @override
  void initState() {
    dev.log("initState");
    super.initState();
    _initRemoteConfig();
  }

  void _initRemoteConfig() async {
    dev.log("_initRemoteConfig");
    setState(() {
      isLoading = true;
    });

    dev.log("remote config: defaults");
    await _remoteConfig.setDefaults(
      {
        "first_name": "Junichi",
        "family_name": "MORI",
        "age": 30,
        "hasChild": false,
      }
    );

    dev.log("remote config: configuration settings");
    await _remoteConfig.setConfigSettings(RemoteConfigSettings(
        fetchTimeout: const Duration(minutes: 1),
        minimumFetchInterval: const Duration(seconds: 10),
    ));

    dev.log("remote config: fetch and activate");
    await _remoteConfig.fetchAndActivate();

    dev.log("call set state");
    setState(() {
      isLoading = false;
    });
  }

コードをみればわかるように、ここでは、

  • デフォルト値の設定
  • RemoteConfig の設定(タイムアウト時間、読み込みインターバル時間など)
  • 実際の値の取得と有効化の呼び出し

を行っています。なお、 dev.log というのは

import 'dart:developer' as dev;

のように定義した dart:developer パッケージのログになります。

RemoteConfig から取得した値(または未取得の場合のデフォルト値)を表示する部分はこんな感じにしておきます。

  @override
  Widget build(BuildContext context) {
    dev.log("build");
    dev.log("isLoading: $isLoading");

    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: isLoading
            ? const CircularProgressIndicator()
            : Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  const Text(
                    'Remote Config contents:',
                  ),
                  Text('First Name: ${_remoteConfig.getString('first_name')}'),
                  Text('Family Name: ${_remoteConfig.getString('family_name')}'),
                  Text('Age: ${_remoteConfig.getInt('age')}'),
                  Text('Has child ?: ${_remoteConfig.getBool('hasChild')}'),
                ],
              ),
      ),
    );
  }

isLoading で Widget を分けて、 RemoteConfig の設定が完了する前であれば、処理中のくるくる回る円を表示するようにします。設定完了後は、 RemoteConfig 経由でパラメータを取得し、その内容を表示するようなコードになっています。

実行

これでアプリ側の準備ができたので、一度実行してみます。エミュレータを立ち上げて、実行すると、下記のように、 RemoteConfig のデフォルト値が表示されます。

実は、この時点ではまだ Firebase プロジェクト側で RemoteConfig パラメータを設定していないのですが、問題なく動作しています。また、表示されている値も、上記のコードで設定したデフォルト値になっています。

Firebase プロジェクトに Remote Config のパラメータを設定

次に、 Firebase Console 側で Remote Config にパラメータを追加して、アプリ側に反映されることを確認してみます。

Firebase Console にログインして、Remote Config を選択します。初めて Remote Config を利用するときだと、

のような画面が表示されます。ここで、『構成を作成』をクリックすると、パラメータの追加画面が表示されます。

パラメータの追加は難しくなく、キー、データ型、デフォルト値を指定すればいいだけです。必要な入力が終わったら、『保存』を押します。この時点では、まだ変更は反映されていないので、ご安心ください。

最初のパラメータ追加後は、上記のような画面に変わります。以後は、『パラメータの追加』ボタンを押して、同様にパラメータを追加していきます。このとき、Firebase Console 側で設定した値が反映されたのがわかりやすいように、最低一つはデフォルト値と異なる値を設定しておきます。

パラメータの設定ができたら、画面上部に表示されている

『変更を公開』ボタンを押しておきます。設定したパラメータの内容はこんな感じです。

氏名を日本語表記にしました。ここまでできたら、アプリをもう一度起動します。すると、

のように、 Firebase Console で設定した値が反映されていることがわかります(氏名が日本語表記になっていると思います)。

変更の反映について

上記の例の場合は、起動時に Remote Config の fetch と activate を行っている(fetchAndActivate を呼び出している)ため、アプリ起動時に内容が反映されていました。起動時以外に RemoteConfig の内容を更新するためには、アプリ側で fetch と activate を適宜呼び出して、 Remote Config を反映させるタイミングを制御してやる必要があります。

これがちょっと面倒なら、 Remote Config のパラメータが変更されたらそれをすぐに反映させる方法もあります。今回はそれを試しました。

変更をすぐに反映させる方法

Firebase コンソール側でのパラメータの変更をアプリ側にすぐに反映させたいこともあるかと思います。その場合は、さきほどの initState 内の _initRemoteConfig のところに、 RemoteConfig が変化したらすぐに反映するための処理を追加しておきます。

    dev.log("remote config: fetch and activate");
    await _remoteConfig.fetchAndActivate();

    _remoteConfig.onConfigUpdated.listen(
        (event) async {
          await _remoteConfig.activate();
          setState(() {});
        }
    );

    dev.log("call set state");

こんな感じですね。Firebase プロジェクト側の Remote Config のパラメータに変更があれば、 _remoteConfig.onConfigUpdated.listen が呼ばれるので、この内部で setState を呼び出して、再描画させる感じです。

準備ができたら、早速試してみます。 Firebase Console 側で Remote Config を選択し、パラメータを変更します。今回は age を 100 にしてみます。

さきほどと同様に、画面上部に表示されている『変更を公開』ボタンを押します。すると、

すぐにエミュレータ内の画面表示がかわりましたね。おぉ、結構すごいな。

RemoteConfig の設定値のダウンロードについて

ここまで試すと、まず一つ思うのが、パラメータのキーとかを間違えないようにするため、 Firebase コンソールで設定した RemoteConfig パラメータの内容をアプリ側のデフォルト値として使えないかということです。

ドキュメントを読むと、プラットフォーム毎に定められたフォーマットで現在の設定値をダウンロードできるようです。

ですが、ここをよく見ると、 Android / iOS / Web があるだけで、 Fluttter に対応するフォーマットはありません。対応するフォーマットは、それぞれ

  • Android -> xml
  • iOS -> plist
  • Web -> json

になっています。

Flutter なのに、デフォルト値の処理を Android / iOS のネイティブ側でやんないといけないのは今一つな感じです。どうしたものかな?と考えたのですが、 Flutter の場合も Web と同様に JSON 形式のファイルをダウンロードし、これを asset としてプロジェクトに含めるようにして、 Flutter のコードで asset の json ファイルを読み込み、これをデコードして、Map に変換し、 RemoteConfig#setDefaults に渡すようにします。

こうしておけば、 Fluttter 配下の JSON ファイルを更新するだけで、プラットフォームに寄らずに、デフォルト値を更新することもできるようになるかと思います。

では、早速やってみます。

Firebase コンソールから対象のプロジェクトを選択し、 RemoteConfig を選択します。『パラメータ』タブ内の設定部分にある

三点ボタンをクリックします。表示されたメニューから、

『デフォルト値をダウンロード』を選択します。すると、下記のようなダイアログが表示されるので、

希望するフォーマットを選択して、『ファイルをダウンロード』ボタンを押します。今回は json フォーマットを選択します。

内容を見てみると、

{
  "age": "30",
  "hasChild": "true",
  "first_name": "純一",
  "family_name": ""
}

こんな感じです。

(参考)警告内容について

なお、上記のファイル形式を選択する際のダイアログは、中央にでかでかと警告が載っています。

警告: アプリ内のデフォルト値を使用するように設定されたパラメータは空になります。この機能を使用する場合は、すべてのパラメータのデフォルト値を構成することをおすすめします。

この日本語だと、一瞬パラメータが空になってしまうのか、と思うかもしれませんが、そうではありません。ここでいう『アプリ内のデフォルト値を使用するように設定されたパラメータ』というのは、パラメータの編集画面を開いた際の

これが有効になっているパラメータのことを指しています。このトグルボタンを有効に変更すると、

このように変化し、パラメータの値を入力できなくなります。この状態になっているパラメータについては、パラメータが空になる、という意味の警告です(値が未設定なので当然ですよね)。

なので、値を入力済みのものが新たに空になるというわけではないので、ご安心ください。

Flutter プロジェクトで読み込み

次に、このファイルを Flutter で扱えるようにします。まずは pubspec.yaml を

# 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
  assets:
    - assets/

のようにして、 assets フォルダ以下を読み込み対象とします。

そのうえで、assets フォルダにさきほどダウンロードした json ファイルをコピーしておきます(もし、 assets フォルダがなければ作成します)。

次は、これをさきほどの設定処理で読み込んで設定するようにします。

    dev.log("remote config: defaults");
//    await _remoteConfig.setDefaults(
//        {
//          "first_name": "Junichi",
//          "family_name": "MORI",
//          "age": 30,
//          "hasChild": false,
//        }
//    );
    String default_data = await rootBundle.loadString("assets/remote_config_defaults.json");
    Map<String, dynamic> json_map = json.decode(default_data);
    await _remoteConfig.setDefaults(json_map);

json への変換は dart:convert パッケージを利用しています。なお、assets からの読み込みと json への変換は下記を参考にしました。

FlutterでローカルのJSONファイルを読み込む #Dart - Qiita

ここまでできたら、一度、 Firebase プロジェクト側のパラメータをすべて削除してから、実行してみます。この場合も、問題なくデフォルト値が表示されているのがわかります。

(参考) Firebase Console プロジェクトの Remote Config 設定の復元

なお、 Firebase プロジェクト側の Remote Config の設定も簡単に復元することができます。

たとえば、上記の削除前に、『現在の構成ファイルをダウンロード』を呼び出し、パラメータや(今回はやってませんが)各種条件の設定などをファイルとしてダウンロードしておきます。

こうすることで、『ファイルから公開』を選択すると、

のように、構成ファイルの json ファイルを指定するように求められます。ここで、さきほどダウンロードした json ファイルを反映することで、パラメータを再度設定することができます。

なかなか便利ですね。

なお、Remote Config のパラメータ等はバージョニングされて管理されており、これら一つのまとまったものを Remote Config テンプレートと呼んでいるようです。

まとめ

実装の見通しさえ一度わかれば、使うのはそんなに難しいもんではなさそうですね。あと、Remote Config で扱えるデータ型として JSON があるので、これを使えば結構大きな初期データの設定にも使うことができそうです。これは、使っていきたいですね。