Flutter: Hide Credential Keys
- Published on
- Authors
- Name
- Mudassir
- Github
- @Lzyct
In some cases, we need to hide some credential keys on our Application like CLIENT_ID
, and CLIENT_SECRET
even for your GOOGLE_MAP_API
key. In my previous post talking about Flutter: Store data based on Environment, we only hide the key from the Dart Code
.
So, in this article. We will be talking about how to hide credential keys in Android and iOS. For example, we will hide our google maps API key which we will use the google_maps_flutter library. You can see the documentation for more detailed implementation.
So let's start! 🚀
Flutter
In the flutter, we need to add our API key to the JSON
file
/// .env.json
{
...
"GOOGLE_MAP_API": "thisIssampleAPIKey"
...
}
And then you can call the API key on the Dart
code.
String.fromEnvironment("GOOGLE_MAP_API")
Android
On Android, we need to parse every variable from --dart-define
and make them available key:value
map to use.
// android/app/build.gradle
...
/// parse dart-define
def dartEnvironmentVariables = []
if (project.hasProperty('dart-defines')) {
dartEnvironmentVariables = project.property('dart-defines')
.split(',')
.collectEntries { entry ->
def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')
[(pair.first()): pair.last()]
}
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
...
With this, we successfully parsed the base64 version of every variable. So, how to use that? We can pass that data to Android resources.
<!--android/app/src/main/res/values/strings.xml -->
<string name="map_key" translatable="false">@string/google_map_api</string>
Don't forget to register the string key first on the build.gradle
// android/app/build.gradle
...
defaultConfig {
// TODO: Specify your unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.app"
minSdkVersion 20
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
multiDexEnabled true
resValue "string", "google_map_api", dartEnvironmentVariables.GOOGLE_MAP_API
...
}
And on AndroidManifest.xml
, we can call the Google Map API key with
<!-- android/app/main/AndroidManifest.xml -->
<application
....>
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="@string/map_key"/>
</application>
iOS
On iOS also need to parse the --dart-define
so the native iOS can read the variables.
First, we need to add a key on ios/Runner/Info.plist
<plist>
<dict>
...
<key>DART_DEFINES</key>
<string>$(DART_DEFINES)</string>
</dict>
</plist>
And, to access the --dart-define
values from Swift
code
// ios/Runner/AppDelegate.swift
import UIKit
import Flutter
import GoogleMaps
import flutter_local_notifications
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Extract variable from --dart-define
let dartDefinesString = Bundle.main.infoDictionary!["DART_DEFINES"] as! String
var dartDefinesDictionary = [String:String]()
for definedValue in dartDefinesString.components(separatedBy: ",") {
let decoded = String(data: Data(base64Encoded: definedValue)!, encoding: .utf8)!
let values = decoded.components(separatedBy: "=")
dartDefinesDictionary[values[0]] = values[1]
}
GMSServices.provideAPIKey(dartDefinesDictionary["GOOGLE_MAP_API"]!)
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as UNUserNotificationCenterDelegate
}
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
That's it! Now we can use your Google Map API key inside our native code without worrying that our keys are exposed in our native code.
Reference: How to setup dart-define for keys and secrets on Android and iOS in Flutter apps