【Flutter】--dart-defineを使用して環境ごとにAPIの接続先を切り替える

# はじめに
Flutterでの環境切り替えには色々方法がありますが今回は```--dart-define```でビルド時にパラメーターを渡して接続を切り替えるシンプルな実装でやってみようと思います

[Flavor](https://pub.dev/packages/flutter_flavor)等のパッケージを使用する場合に比べ、設定や新規で作成するファイルが少なくなるため(多分)
小さいアプリなら簡単に対応できるというメリットがあるかなという所感

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [実装後のイメージ](#実装後のイメージ)
1. [Flutter側の実装](#Flutter側の実装)
1. [動作確認](#動作確認)
1. [リリース時のコマンド](#リリース時のコマンド)
1. [最後に](#最後に)
1. [参考文献](#参考文献)

<!-- 各チャプター -->
# 実装後のイメージ
起動時のファーストビューで接続先(url文字列)が画面に表示されているようにして
環境の切り替えができている確認とします
本来は開発環境、テスト環境、本番環境ぐらいには分けたいですが、
今回は簡単に開発環境(dev)本番環境(prod)の二つで試してみます

以下のようなコマンドで起動、環境の切り替えができるようにします
`flutter run --dart-define=FLAVOR=dev`

# Flutter側の実装

 ### 環境の定義
 [flutter_dotenv](https://pub.dev/packages/flutter_dotenv)を使用して
 各環境の接続先を環境変数として定義します
 ```:.env
 # dev(開発)環境
 DEFINE_DEV_BASE_URL='http://development.api.qiita.demo/api'

 # production（本番）環境
 DEFINE_PROD_BASE_URL='http://production.api.qiita.demo/api'
 ```
 
pubspec.yamlに.envファイルを記載してflutter側でファイルを読み込めるようにします
```yaml:pubspec.yaml
assets:
    - .env
```

### 環境を切り替えるロジックを実装
`String.fromEnvironment('FLAVOR');`の箇所で`--dart-define`で指定した
文字列を取得
取得した文字列ごとに分岐し、異なる環境変数を格納しているのでソースは後述しますがflutter側からは`environment['baseUrl']`でアクセスすることができます
```dart:api_environment.dart
import 'package:flutter_dotenv/flutter_dotenv.dart';

const String flavor = String.fromEnvironment('FLAVOR');

final variablesDev = {
  'baseUrl': dotenv.env['DEFINE_DEV_BASE_URL'],
};

final variablesProd = {
  'baseUrl': dotenv.env['DEFINE_PROD_BASE_URL'],
};

Map<String, dynamic> get environment {
  if (flavor == 'dev') {
    return variablesDev;
  }
  if (flavor == 'prod') {
    return variablesProd;
  }
  throw UnimplementedError('baseUrl: $flavor is unknown value');
}
```

### 接続先のURLを取得する
`environment['baseUrl']`で接続先のURLを取得
```dart:main.dart
import 'package:api_env_demo/api_environment.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';

void main() async {
  // .envファイルを読み込む
  await dotenv.load(fileName: '.env');
  // ...runapp
}

// ~~~~~~省略~~~~~

  @override
  Widget build(BuildContext context) {
    // 接続先のURLを取得する
    final String path = environment['baseUrl'].toString();
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(path, 
              style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 30),)
          ],
        ),
      ),
    );
  }

```

# 動作確認
以下のコマンドで実行
dev(開発)環境の場合
`flutter run --dart-define=FLAVOR=dev`

prod(本番)環境の場合
`flutter run --dart-define=FLAVOR=prod`

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3126590/d8e08e4f-ccc5-f151-1cd7-0d0efe63046a.png" width="200" height=456px>　<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3126590/6c839c6f-89ca-466f-94af-dc48cd3ee749.png" width="200" height=456px>

`--dart-define`で指定した環境のURLを正常に取得できていそうです

### デバッグする時
実際にローカルで開発している時デバッグしたりすると思うのですが
その時の設定に関してです

VSCodeでの設定の例を記載
launch.jsonの設定を以下のように記載
```json:launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "api_env_demo_dev",
            "cwd": "api_env_demo",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--dart-define=FLAVOR=dev",
            ]
        },
        {
            "name": "api_env_demo_prod",
            "cwd": "api_env_demo",
            "request": "launch",
            "type": "dart",
            "flutterMode": "debug",
            "args": [
                "--dart-define=FLAVOR=prod",
            ]
        },
    ]
}

```

# リリース時のコマンド
リリースの時でも同じようにビルドコマンドに`--dart-define`のオプションを指定するだけで任意の環境で配布することができます
例）iOS
```flutter build ipa --dart-define=FLAVOR=dev --export-options-plist=ios/ExportOptions.plist```

ローカルでのビルドと違い、iOSやAndroid等のプラットフォーム側にオプションを伝える必要があるので別途設定が必要になってきます
この辺の詳細に関しては以下の記事がすごく参考になりました
[【Flutter 3.7以上】Dart-define-from-fileを使って開発環境と本番環境を分ける](https://zenn.dev/altiveinc/articles/separating-environments-in-flutter#%E5%BD%93%E8%A8%98%E4%BA%8B%E3%81%A7%E4%BE%8B%E3%81%A8%E3%81%97%E3%81%A6%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E7%92%B0%E5%A2%83%E5%88%86%E3%81%91%E6%83%85%E5%A0%B1)

# 最後に
リリース時のアイコンや名前も環境ごとに切り替えることを考えると他に最適なやり方があるのかなと思いつつも
現状問題がないのでこのような感じで環境を切り替えて日々開発しています
今後もっといい方法を見つけたら書いてみたいと思います

# 参考文献
- [Flutterで環境ごとにビルド設定を切り替える — iOS編](https://medium.com/flutter-jp/flavor-b952f2d05b5d)