Flutterで環境変数を使いたい
iOS,Androidでどうやって読み込む?
環境変数で隠したAPI KEYをネイティブ側のコードで読み込むには、Dart-defineではできないそうです。
どうやって読み込むかというかと、ネイティブ側のコードをいじる必要があります😅
これが結構難しくて、iOSだと、作成したファイルをx-code開いてから、ドラッグ&ドロップで配置しないと、NGみたいです。プロジェクトのフォルダの中に直接作ったファイルを配置すると、よくわからないエラーが出てきて、パニックになりました😱
Flutterで環境変数を使う
iOS,Androidで、API KEYを隠している環境変数を読み込むには、ネイティブ側で設定をする必要があります。
iOSの場合
ios/Runner/配下に、Environment.swift作成して配置しますが、こちらのファイルは、x-codeを開いて、ファイルをドラッグ&ドロップで配置しないと、iOS独特の謎のエラーが発生する原因になるようです。
import Foundation
struct Env {
static let googleMapApiKey = "API_KEY_HERE"
}
ios/Runner/内に配置してあるAppDelegete.swiftに、今回は環境変数の読み込みをするGoogleMapAPIのコードを追加しました。
import UIKit
import Flutter
import GoogleMaps
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GMSServices.provideAPIKey(Env.googleMapApiKey)
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Androidの場合
androidディレクトリ配下にsecret.propertiesを作成します。
API KEYは、""で囲まなくてOKです。
android/secret.properties
googleMap.apiKey=API_KEY_HERE
android/app/配下のbuild.gradleに環境変数を読み込めるように、設定を追加します。
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
// 環境変数を呼び出すコード
def secretProperties = new Properties()
def secretPropertiesFile = rootProject.file('secret.properties')
if (secretPropertiesFile.exists()) {
secretPropertiesFile.withReader('UTF-8') { reader ->
secretProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 33 // 33を指定
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.api_env_app"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdkVersion 21 // 21を指定
targetSdkVersion 33 // 33を指定
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
// 環境変数を呼び出すコード
manifestPlaceholders = [
googleMapApiKey: secretProperties.getProperty('googleMap.apiKey'),
]
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
android/app/src/main/配下のAndroidManifest.xmlで環境変数を読み込めるように設定を追加します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.api_env_app">
<!-- 権限の追加 ここから-->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<!-- ここまで -->
<!-- android:name="${applicationName}を削除する" -->
<application
android:label="api_env_app"
android:icon="@mipmap/ic_launcher">
<!-- google_maps_flutterのAPI KEYの設定 ここから-->
<meta-data android:name="com.google.android.geo.API_KEY" android:value="${googleMapApiKey}"/>
<!-- ここまで-->
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
.gitignoreにAPI KEYを貼り付けたコードをコミットの対象から外す設定をする
/android/secret.properties
# iOS
/ios/Runner/Environment.swift
main.dartに地図を表示するだけのコードを記入します。
こちらのチュートリアルの内容を途中まで進めています。
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late GoogleMapController mapController;
final LatLng _center = const LatLng(45.521563, -122.677433);
void _onMapCreated(GoogleMapController controller) {
mapController = controller;
}
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Maps Sample App'),
backgroundColor: Colors.green[700],
),
body: GoogleMap(
onMapCreated: _onMapCreated,
initialCameraPosition: CameraPosition(
target: _center,
zoom: 11.0,
),
),
),
);
}
}
ビルドしたスクリーンショット
読み込めているので、ビルドに成功しました。
Android
iOS
まとめ
こちらに完成品のソースコードを共有しておきます。コミットの対象から外しているファイルは表示されていないので見れません。
Discussion
AndroidManifext
ではなくAndroidManifest
です。パスを手入力されたんですか?shinriyoさん、ご指摘ありがとうございました。
修正いたしました。