Armazenando suas chaves secretas no Flutter
A maneira mais segura de proteger suas informações confidenciais é não incluí-las em seu aplicativo. Mesmo em desenvolvimento nativo (Android, por exemplo), são necessárias etapas extras para impedir que alguém recupere as chaves, desmontando o programa. Estou escrevendo isso porque enfrentei o mesmo problema no Flutter, como outras pessoas, que procuravam algo semelhante a propriedades locais no Android.
A abordagem mais recomendada que encontrei é usar recursos de texto. No Flutter você só precisa carregar seu arquivo contendo suas chaves secretas como se estivesse carregando qualquer outro ativo.
Por exemplo,
import 'dart:async' show Future; import 'package:flutter/services.dart' show rootBundle; Future<String> loadAsset() async { return await rootBundle.loadString('assets/config.json'); }
Como é mostrado aqui nos documentos do Flutter: https://flutter.io/assets-and-images/#loading-text-assets
Esse é o caminho mais curto. Você pode carregar um JSON, analisá-lo com dart:convert e ter suas chaves. Mas para este exemplo, vamos torná-lo um pouco mais elaborado.
Primeiro, vamos criar um arquivo chamado secrets.json
que manterá nossas chaves de API secretas. E armazená-lo no diretório raiz do nosso projeto. Lembre-se de não submeter seu arquivo secrets.json
ao controle de versão.
{ "api_key": "random_api_key" }
Em seguida, precisamos escrever uma entrada em pubspec.yaml
apontando para nosso arquivo secreto.
assets: - secrets.json
Agora, vamos definir a classe que vai guardar nossas chaves, digamos que se chama Secret.
class Secret { final String apiKey; Secret({this.apiKey = ""}); factory Secret.fromJson(Map<String, dynamic> jsonMap) { return new Secret(apiKey: jsonMap["api_key"]); } }
Em seguida, um SecretLoader
.
import 'dart:async' show Future; import 'dart:convert' show json; import 'package:flutter/services.dart' show rootBundle;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; }); } }
Eu usei loadStructuredData
porque esta função já foi projetada para esse caso de uso, você só precisa enviar um parser.
Depois disso, você pode usar o SecretLoader
assim:
Future<Secret> secret = SecretLoader(secretPath: "secrets.json").load();
A partir daí, cabe a você como usar esse conhecimento. Se estiver usando streams, você pode convertê-los em streams com .toStream()
. Ou você pode usá-lo em sua árvore de widgets raiz com o FutureBuilder
.