This article explains how to avoid hardcoding your API key when running Flutter on the web.
Normally, it works fine if you write the key directly into index.html.
<body>
<script src="flutter_bootstrap.js" async></script>
<script>
const apiKey = "{{API_KEY}}"; // Dartコードで置換
const script = document.createElement('script');
script.src = `https://maps.googleapis.com/maps/api/js?key=ここにAPI KEYベタ打ち`;
document.head.appendChild(script);
</script>
</body>However, this means the key can no longer be safely managed with Git, so you'll want to read it from an .env file instead. Unfortunately, index.html cannot reference .env directly.
Solution
Add platform-specific logic
For the web, we'll load a separate file. I created a utils directory directly under lib, and added web_utils.dart inside it.
Splitting the file out this way lets us use
import ‘dart:html’ as html;
without any issues.
This is necessary because that package only works on the web, so it can't be included in code that also targets other platforms.
// ignore: avoid_web_libraries_in_flutter
import 'dart:html' as html;
void addGoogleMapsScript(String apiKey) {
final script = html.ScriptElement()
..src = 'https://maps.googleapis.com/maps/api/js?key=$apiKey&callback=initMap'
..type = 'text/javascript'
..async = true
..defer = true;
html.document.body!.append(script);
}We also prepare a file with the logic for platforms other than the web (iOS, Android). I created stub_utils.dart in the utils directory, written as follows.
void addGoogleMapsScript(String apiKey) {
// モバイルプラットフォームでは何もしない
}Handling this in main.dart
Add the flutter_dotenv package.
flutter pub add flutter_dotenvimport 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'firebase_options.dart';
import 'widgets/bottom_nav_layout.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
// 条件付きインポート
import 'utils/web_utils.dart'
if (dart.library.html) 'utils/web_utils.dart'
if (dart.library.io) 'utils/stub_utils.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 環境変数を読み込む
await dotenv.load(fileName: ".env");
// Firebaseの初期化
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
// Webの場合のみGoogle Mapsスクリプトを追加
if (kIsWeb) {
final apiKey = dotenv.env['GOOGLE_MAPS_API_KEY'];
if (apiKey != null && apiKey.isNotEmpty) {
addGoogleMapsScript(apiKey);
} else {
print('Error: GOOGLE_MAPS_API_KEY is not set in the .env file.');
}
}
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Anonymous Login Demo',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
),
home: const BottomNavLayout(currentIndex: 0),
);
}
}Add a web app in the Firebase console
In the Firebase console, add a web app alongside your mobile apps, and issue an API key.
Add this key to your .env file.
Setting up the .env file
Configure it as shown below.
Wherever you see api_key_here, enter the API key you just created.
GOOGLE_MAPS_API_KEY=your_google_maps_api_key_here
# Firebase iOS Configuration
FIREBASE_IOS_API_KEY=your_firebase_ios_api_key_here
FIREBASE_IOS_APP_ID=your_firebase_ios_app_id_here
FIREBASE_IOS_MESSAGING_SENDER_ID=your_firebase_ios_messaging_sender_id_here
FIREBASE_IOS_PROJECT_ID=your_firebase_ios_project_id_here
FIREBASE_IOS_STORAGE_BUCKET=your_firebase_ios_storage_bucket_here
FIREBASE_IOS_BUNDLE_ID=your_firebase_ios_bundle_id_here
# Firebase Web Configuration
FIREBASE_WEB_API_KEY=your_firebase_web_api_key_here
FIREBASE_WEB_AUTH_DOMAIN=your_firebase_web_auth_domain_here
FIREBASE_WEB_PROJECT_ID=your_firebase_web_project_id_here
FIREBASE_WEB_STORAGE_BUCKET=your_firebase_web_storage_bucket_here
FIREBASE_WEB_MESSAGING_SENDER_ID=your_firebase_web_messaging_sender_id_here
FIREBASE_WEB_APP_ID=your_firebase_web_app_id_here
FIREBASE_WEB_MEASUREMENT_ID=your_firebase_web_measurement_id_hereFirebase configuration: firebase_options.dart
This is used to select the right API key for each platform.
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart' show defaultTargetPlatform, kIsWeb, TargetPlatform;
import 'package:flutter_dotenv/flutter_dotenv.dart';
/// Default [FirebaseOptions] for use with your Firebase apps.
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
throw UnsupportedError('DefaultFirebaseOptions have not been configured for android.');
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
throw UnsupportedError('DefaultFirebaseOptions have not been configured for macos.');
case TargetPlatform.windows:
throw UnsupportedError('DefaultFirebaseOptions have not been configured for windows.');
case TargetPlatform.linux:
throw UnsupportedError('DefaultFirebaseOptions have not been configured for linux.');
default:
throw UnsupportedError('DefaultFirebaseOptions are not supported for this platform.');
}
}
static final FirebaseOptions ios = FirebaseOptions(
apiKey: dotenv.env['FIREBASE_IOS_API_KEY']!,
appId: dotenv.env['FIREBASE_IOS_APP_ID']!,
messagingSenderId: dotenv.env['FIREBASE_IOS_MESSAGING_SENDER_ID']!,
projectId: dotenv.env['FIREBASE_IOS_PROJECT_ID']!,
storageBucket: dotenv.env['FIREBASE_IOS_STORAGE_BUCKET']!,
iosBundleId: dotenv.env['FIREBASE_IOS_BUNDLE_ID']!,
);
static final FirebaseOptions web = FirebaseOptions(
apiKey: dotenv.env['FIREBASE_WEB_API_KEY']!,
authDomain: dotenv.env['FIREBASE_WEB_AUTH_DOMAIN']!,
projectId: dotenv.env['FIREBASE_WEB_PROJECT_ID']!,
storageBucket: dotenv.env['FIREBASE_WEB_STORAGE_BUCKET']!,
messagingSenderId: dotenv.env['FIREBASE_WEB_MESSAGING_SENDER_ID']!,
appId: dotenv.env['FIREBASE_WEB_APP_ID']!,
measurementId: dotenv.env['FIREBASE_WEB_MEASUREMENT_ID'],
);
}