メモ2ブログ

メモtoウェブログ。旧ブログはこちら。 http://sakebook.blogspot.jp/

Flutterで秘匿情報を扱う

最近Flutterを触ってます。

Gitなどに載せたくない情報の扱い方についてのメモ。2つの方法を取り上げます。

.env + flutter_dotenv

flutter_dotenvを使います。pubspec.ymlに以下を追加して

  • pubspec.yml
dependencies:
  ..
  flutter_dotenv: ^2.0.1
..
flutter:
  ..
  assets:
  - .env
$ flutter pub get

を実行します。

上の例ではプロジェクトルートに .envというファイルを置く想定です。

  • .env
SAMPLE_VALUE=this is dot env value.

main関数のはじめに初期化します。

void main() async {
  await DotEnv().load('.env');
  runApp(DotenvApp());
}

次のように呼び出します。

class DotenvApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Dotenv App"),
        ),
        body: Center(
          child: Column(
            children: <Widget>[
              Text(
                'dotenv values: ${DotEnv().env["SAMPLE_VALUE"]}',
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Map<String, String>型なので取り出して適宜キャストして利用します。

一度初期化したらどこからでも呼べます

flutterパッケージに依存しているのでFlutter for Webでは利用できません。

JSON + json_serializable

json_serializableを使います。pubspec.ymlに以下を追加して

  • pubspec.yml
dependencies:
  ..
  json_annotation: ^2.4.0
..
dev_dependencies:
  ..
  build_runner: ^1.0.0
  json_serializable: ^3.0.0
..
flutter:
  ..
  assets:
  - secret.json
$ flutter pub get

を実行します。

上の例ではプロジェクトルートに secret.jsonというファイルを置く想定です。

{
  "value": "secret value",
  "num":  20190606
}

JSONは末尾カンマを許してはくれないので注意しましょう

対応するモデルを作成します。

part 'secret.g.dart';

@JsonSerializable()
class Secret {
  final String value;
  final int num;

  const Secret(this.value, this.num);

  factory Secret.fromJson(Map<String, dynamic> json) => _$SecretFromJson(json);
}

このままだと自動生成されたコードがないので、エラーになります。コードを生成します。

$ flutter pub run build_runner build

これでエラーは消えます。

JSONファイルを読み込んでモデルを返すクラスを用意します。

  • secret_loader.dart
class SecretLoader {
  final String secretPath;
  SecretLoader({this.secretPath});

  Future<Secret> load() {
    return rootBundle.loadStructuredData<Secret>(this.secretPath,
        (jsonStr) async {
      final secret = Secret.fromJson(json.decode(jsonStr));
      return secret;
    });
  }
}

準備はできました。次のように呼び出します。

class SecretApp extends StatelessWidget {
  final SecretLoader loader = SecretLoader(secretPath: "secret.json");

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Secret App"),
        ),
        body: FutureBuilder<Secret>(
          future: loader.load(),
          builder: (context, snapshot) {
            if (!snapshot.hasData) {
              return Center(
                child: CircularProgressIndicator(),
              );
            }
            final secret = snapshot.data;
            return Center(
              child: Column(
                children: <Widget>[
                  Text(
                    'secret value: ${secret.value}',
                  ),
                  Text(
                    'secret num: ${secret.num}',
                  ),
                ],
              ),
            );
          },
        ),
      ),
    );
  }
}

SecretLoader#loadの返り値がFuture型なのでFutureBuilderを使ってます。

内部で保持する仕組みはないので、その他の変数と同じように呼び出したスコープ内でしか扱えません

flutter_dotenvのときのようにmainで呼び出して、コンストラクタで必要なWidgetに値を渡しても良いと思います。

flutterパッケージに依存していないのでFlutter for Webでも利用できます。その際はJSONファイルをweb/assets/に配置します。

まとめ

どちらも一長一短あります。秘匿情報の利用が初期化時のみで済むのならJSON + json_serializableの方が、若干の準備がありますが良いかなと思いました。構造を持てるのは大きな魅力です。さっと試す程度なら.env + flutter_dotenvのほうが手軽だと思います。

どちらの方法にしても、間違えて秘匿情報を書いたファイルをコミットしないように注意しましょう。

参考

flutter_dotenv / Flutter Package

Storing your secret keys in Flutter – Sócrates Díaz Severino – / Medium

JSON and serialization / Flutter