Load Secret Api Keys with Flutter without git file check in

Load Secret Api Keys with Flutter without git file check in

Every app has some api keys or secrets that should not be checked into git. Here is how i've implemented the Keyloader for Flutter.

Alexander Thiele · 2 minute read

Assets Key File

First, you need to find a folder for your API keys. In this example, we will use the assets folder for our keys.

We need to specify the assets folder and file in our pubspec.yaml:

flutter: assets: - assets/apikey.json

Git ignore

We also don't want to commit the file to our git repository. That‘s why we will add the file to our .gitignore file:

/assets/apikey.json

Load the Key in Flutter

The content of apikey.json could be in the following format:

{ "linkfive_api_key": "TmljZSAyIG1lZXQgeW91IE1yLkhhY2tlcg=" }

We need to load the file in our app and wait for the result. Let's create the Keys.dart and KeyLoader.dart classes which handles the whole loading process:

Keys data class that holds the keys:

class Keys { final String linkFiveApiKey; Keys({this.linkFiveApiKey = ""}); factory Keys.fromJson(Map<String, dynamic> jsonMap) { return new Keys(linkFiveApiKey: jsonMap["linkfive_api_key"]); } }

KeyLoader class that loads the key file:

class KeyLoader { final String keyPath = "assets/apikey.json"; KeyLoader(); Future<Keys> load(BuildContext context) async { String data = await DefaultAssetBundle.of(context).loadString(keyPath); final jsonResult = json.decode(data); return Keys.fromJson(jsonResult); } }

Load the File

We can then savely load the file in our main method. NOTE: we need a BuildContext since we're accessing the assets folder.

@override void initState() { KeyLoader().load(context); super.initState(); }

We could also wait for the keys to load with a completer:

class MyAppState extends State<MyApp> { Completer<Keys> _keysCompleter = Completer<Keys>(); late Future<Keys> _keysFuture; MyAppState() { _keysFuture = _keysCompleter.future; } @override void initState() { _keysCompleter.complete(KeyLoader().load(context)); super.initState(); } @override Widget build(BuildContext context) { return FutureBuilder<Keys>( future: _keysFuture, builder: (BuildContext context, AsyncSnapshot<Keys> snapshot) { if (snapshot.hasData) { // Key Loaded return Container(); } // Still Loading return Container( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [CircularProgressIndicator()], ), ); }); } }