くらげになりたい。

くらげのようにふわふわ生きたい日曜プログラマなブログ。趣味の備忘録です。

VSCodeでFlutterプロジェクトを新しく作るときにやること

今までAndroid StudioでFlutterしてたけど、
VSCodeでやってみたときの備忘録(*´ω`*)

新規プロジェクトを作成するときいつも忘れるのでまとめておく。

事前準備

flutterとfvmはインストール済みの想定。

flutterのバージョン確認(fvmの設定)

# インストール済みバージョンの確認
$ fvm list
# リリースされているバージョンの確認
$ fvm releases

# 使いたいバージョンのインストール
$ fvm install 3.10.5

# 使うバージョンの設定
#(プロジェクト作成前なので--force)
$ fvm use 3.10.5 --force

# 現在のバージョンの確認
$ fvm list

# 環境のチェック
$ fvm flutter doctor
3.10.5 (active)
2.10.5

VSCodeの設定

.fvmの除外と自動フォーマットの有効化をしておく。

// .vscode/settings.json
{
  "dart.flutterSdkPath": ".fvm/flutter_sdk",
  "[dart]": {
    "editor.defaultFormatter": "Dart-Code.dart-code",
    "editor.codeActionsOnSave": { "source.fixAll": true }
  },
  "search.exclude": {
    "**/.fvm": true
  },
  "files.watcherExclude": {
    "**/.fvm": true
  }
}
$ gibo dump macOS VisualStudioCode Android JetBrains Vim Java Kotlin Node Swift Dart >> .gitignore
$ echo ".fvm/flutter_sdk" >> .gitignore

プロジェクトの作成

# 現在のバージョンの確認
$ fvm list

# プロジェクトの作成
$ fvm flutter create .

テスト起動

バイスの選択は下の方に。

実行は、lib/main.dartを開いて、
右上の方にある。

実行すると、停止やリロードなどのパネルが表示される。

flutter_flavorizrでflavorを用意

# dev_dependenciesにインストール
$ fvm flutter pub add -d flutter_flavorizr

設定ファイル(flavorizr.yaml)を用意。

アイコンなどはwebにも対応しているflutter_launcher_iconsで行うため、
instructionsを少し変更している。

Firebaseを使う場合は、firebase.configのパスに資材を置いておく。

### flavorizr.yaml
### flutter_flavorizr
# Doc: https:#pub.dev/packages/flutter_flavorizr
flavors:
  dev:
    app:
      # アプリ名
      name: "DEV: My Sample App"
    # Androidの設定
    android:
      applicationId: "com.example.debug"
      firebase:
        config: ".firebase/debug/google-services.json"
    # iOSの設定
    ios:
      bundleId: "com.example.dev"
      firebase:
        config: ".firebase/debug/GoogleService-Info.plist"
    # macOSの設定
    macos:
      bundleId: "com.example.debug"
  stag:
    app:
      name: "STAG: My Sample App"
    android:
      applicationId: "com.example.stag"
      firebase:
        config: ".firebase/stag/google-services.json"
    ios:
      bundleId: "com.example.stag"
      firebase:
        config: ".firebase/stag/GoogleService-Info.plist"
    macos:
      bundleId: "com.example.stag"
  prod:
    app:
      name: "My Sample App"
    android:
      applicationId: "com.example"
      firebase:
        config: ".firebase/prod/google-services.json"
    ios:
      bundleId: "com.example"
      firebase:
        config: ".firebase/prod/GoogleService-Info.plist"
    macos:
      bundleId: "com.example"

# Default is https://github.com/AngeloAvv/flutter_flavorizr/blob/v2.2.1/lib/src/processors/processor.dart
instructions:
  ### Prepare
  - assets:download
  - assets:extract
  ### Android
  - android:androidManifest
  - android:buildGradle
  # - android:dummyAssets
  # - android:icons
  ### Flutter
  - flutter:flavors
  - flutter:app
  - flutter:pages
  - flutter:main
  - flutter:targets
  ### iOS
  - ios:xcconfig
  - ios:buildTargets
  - ios:schema
  # - ios:dummyAssets
  # - ios:icons
  - ios:plist
  # - ios:launchScreen
  ### macOS
  - macos:xcconfig
  - macos:configs
  - macos:buildTargets
  - macos:schema
  # - macos:dummyAssets
  # - macos:icons
  - macos:plist
  ### Google
  - google:firebase
  ### Huawei
  - huawei:agconnect
  ### Cleanup
  - assets:clean
  ### IDE
  - ide:config

あとは、実行して資材の配置/変更などを反映する。

# main.dartなどが上書きされるので、基本は初回の1度だけ
$ fvm flutter pub run flutter_flavorizr

実行すると、lib/配下はこんな感じのファイルを用意してくれる。

lib/
├── pages
│   └── my_first_page.dart
├── app.dart
├── flavors.dart
├── main.dart
├── main_dev.dart
├── main_prod.dart
└── main_stag.dart

ほかを更新したい場合は、-pで実行するタスクを指定するといい。

# firebase資材の更新
$ fvm flutter pub run flutter_flavorizr -p google:firebase
# アプリ名/applicationIdの更新: Android
$ fvm flutter pub run flutter_flavorizr -p android:buildGradle
# アプリ名/applicationIdの更新: iOS
$ fvm flutter pub run flutter_flavorizr -p assets:download,assets:extract,ios:xcconfig,ios:buildTargets,assets:clean
# アプリ名/applicationIdの更新: macOS
$ fvm flutter pub run flutter_flavorizr -p assets:download,assets:extract,macos:xcconfig,macos:buildTargets,assets:clean

ただ、iOS/Android/macOSのみのため、
Web/Windows/Linuxについては手動で変更する必要がある。

.vscode/launch.jsonの設定

--flavorオプションをつけずに起動すると失敗する。。

flavorごとに簡単に起動できるように、
.vscode/launch.jsonを作成。

各環境ごとに用意

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Dev-Debug",
      "request": "launch",
      "type": "dart",
      "program": "lib/main_dev.dart",
      "args": ["--debug", "--flavor", "dev"]
    },
    {
      "name": "Dev-Release",
      "request": "launch",
      "type": "dart",
      "program": "lib/main_dev.dart",
      "args": ["--flavor", "dev"]
    },
    // ...略
  }
}

F5(デバッグ実行)か^F5(デバッグなし実行)で起動できる。

左下から構成の変更が可能。

作成された資材

flavorのファイル。
flavorに応じた設定値などはここに記載するとよい。
(API URLなどのパラメタとか)

// flavors.dart
enum Flavor {
  dev,
  stag,
  prod,
}

class F {
  static Flavor? appFlavor;

  static String get name => appFlavor?.name ?? '';

  static String get title {
    switch (appFlavor) {
      case Flavor.dev:
        return 'DEV: My Sample App';
      case Flavor.stag:
        return 'STAG: My Sample App';
      case Flavor.prod:
        return 'My Sample App';
      default:
        return 'title';
    }
  }
}

起動用のファイル。ここでFlavor.devとかを指定。
main.dartを経由して実行。

// main_dev.dart
import 'flavors.dart';

import 'main.dart' as runner;

Future<void> main() async {
  F.appFlavor = Flavor.dev;
  await runner.main();
}

flutter_launcher_iconsでアイコン作成

# インストール
$ fvm flutter pub add -d flutter_launcher_icons

$ touch flutter_launcher_icons-{dev,stag,prod}.yaml

flutter_launcher_icons-dev.yamlはこんな感じ。

### flutter_launcher_icons
# Doc: https://pub.dev/packages/flutter_launcher_icons
#
# $ fvm flutter pub run flutter_launcher_icons
flutter_launcher_icons:
  # common
  image_path: "assets/launcher_icon/icon-1024x1024.png"
  # android
  android: true
  min_sdk_android: 21 # android min sdk min:16, default 21
  adaptive_icon_background: "#FF99A3" # color or image path
  adaptive_icon_foreground: "assets/launcher_icon/icon-foreground-432x432.png" # only available for Android 8.0 devices and above
  # ios
  ios: true
  remove_alpha_ios: true
  background_color_ios: "#ffffff"

  web:
    generate: true
    # image_path: "path/to/image.png"
    background_color: "#ffffff" # in web/manifest.json
    theme_color: "#FF99A3" # in web/manifest.json
  windows:
    generate: true
    # image_path: "path/to/image.png"
    icon_size: 48 # min:48, max:256, default: 48
  macos:
    generate: true
    # image_path: "path/to/image.png"

Androidのadaptive アイコンについてはこちら。

各ファイルが準備できたら、いかを実行すれば、
サイズ変更と配置をしてくれる。

$ fvm flutter pub run flutter_launcher_icons

以上! これでプロジェクト作成と各環境の準備はOK(*´ω`*)

おまけ: FlavorBanner

開発時などのバナーのWidget。あると実行中の環境がわかって便利。

// flavor_banner.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:start_fluttter/flavors.dart';

class FlavorBanner extends HookConsumerWidget {
  const FlavorBanner({super.key, required this.child});
  final Widget child;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    if (!kDebugMode) return child;

    const color = Colors.red;
    final textColor = HSLColor.fromColor(color).lightness < 0.8
        ? Colors.white
        : Colors.black87;
    return Directionality(
      textDirection: TextDirection.ltr,
      child: Banner(
        location: BannerLocation.bottomStart,
        message: F.name,
        color: color,
        textStyle: TextStyle(
          color: textColor,
          fontSize: 12.0 * 0.85,
          fontWeight: FontWeight.w900,
          height: 1.0,
        ),
        child: child,
      ),
    );
  }
}

こんな感じでMaterialAppを包んで使う。

// app.dart
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'flavor_banner.dart';
import 'flavors.dart';

class App extends HookConsumerWidget {
  const App({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return FlavorBanner(
      child: MaterialApp.router(
        title: F.title,
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        // ...
    );
  }
}