FlutterFire CLI & Firebase Options
FlutterFire CLI & Firebase Options
Manually copying API keys, project IDs, and configuration values from the Firebase console into your Flutter app is error-prone and tedious. The FlutterFire CLI solves this problem by auto-generating a firebase_options.dart file that contains a typed FirebaseOptions object for every target platform. You simply point the CLI at your Firebase project and it does the heavy lifting.
Installing the FlutterFire CLI
The FlutterFire CLI is a Dart-based command-line tool published on pub.dev. Install it globally alongside the Firebase CLI:
Installing the CLI tools
# Install (or update) the Firebase CLI via npm
npm install -g firebase-tools
# Log in to Firebase
firebase login
# Install the FlutterFire CLI globally
dart pub global activate flutterfire_cli
# Verify the installation
flutterfire --version
~/.pub-cache/bin is in your PATH so the flutterfire command is available globally. On macOS/Linux, add export PATH="$PATH":"$HOME/.pub-cache/bin" to your shell profile (.zshrc or .bashrc).Running flutterfire configure
Navigate to the root of your Flutter project (where pubspec.yaml lives) and run:
Generating firebase_options.dart
# Interactive mode — prompts you to select a Firebase project and platforms
flutterfire configure
# Non-interactive mode — specify project and platforms explicitly
flutterfire configure \
--project=my-app-prod \
--platforms=android,ios,web
The CLI will:
- Fetch your Firebase project configuration from the Firebase API
- Register your app for each selected platform (Android, iOS, Web, macOS) if not already registered
- Download
google-services.json(Android) andGoogleService-Info.plist(iOS) into the correct native directories - Generate
lib/firebase_options.dartcontaining aDefaultFirebaseOptionsclass
flutterfire configure any time you add a new platform or change Firebase services. The CLI safely overwrites firebase_options.dart and the native files without touching your Dart business logic.Anatomy of firebase_options.dart
The generated file exports a DefaultFirebaseOptions class with a static method currentPlatform. This method returns the correct FirebaseOptions object at runtime based on defaultTargetPlatform:
Generated firebase_options.dart (simplified)
// File generated by FlutterFire CLI — DO NOT edit manually.
// ignore_for_file: type=lint
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
show defaultTargetPlatform, kIsWeb, TargetPlatform;
class DefaultFirebaseOptions {
static FirebaseOptions get currentPlatform {
if (kIsWeb) {
return web;
}
switch (defaultTargetPlatform) {
case TargetPlatform.android:
return android;
case TargetPlatform.iOS:
return ios;
case TargetPlatform.macOS:
return macos;
default:
throw UnsupportedError(
'DefaultFirebaseOptions are not supported for this platform.',
);
}
}
static const FirebaseOptions android = FirebaseOptions(
apiKey: 'AIzaSy....',
appId: '1:123456789:android:abcdef',
messagingSenderId: '123456789',
projectId: 'my-app-prod',
storageBucket: 'my-app-prod.appspot.com',
);
static const FirebaseOptions ios = FirebaseOptions(
apiKey: 'AIzaSy....',
appId: '1:123456789:ios:abcdef',
messagingSenderId: '123456789',
projectId: 'my-app-prod',
storageBucket: 'my-app-prod.appspot.com',
iosClientId: 'com.example.myapp',
iosBundleId: 'com.example.myapp',
);
static const FirebaseOptions web = FirebaseOptions(
apiKey: 'AIzaSy....',
appId: '1:123456789:web:abcdef',
messagingSenderId: '123456789',
projectId: 'my-app-prod',
storageBucket: 'my-app-prod.appspot.com',
authDomain: 'my-app-prod.firebaseapp.com',
measurementId: 'G-XXXXXXXX',
);
}
Initializing Firebase in main.dart
With firebase_options.dart generated, initializing Firebase in your app is a single call. Always await it inside an async main before running the widget tree:
Wiring Firebase.initializeApp() in main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
Future<void> main() async {
// Ensure Flutter engine is ready before calling native plugins
WidgetsFlutterBinding.ensureInitialized();
// Pass the platform-specific options from the generated file
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp());
}
Firebase.initializeApp() without await. Calling it synchronously returns a Future that is never resolved, so every subsequent Firebase call (Firestore, Auth, etc.) will throw a FirebaseException with the message "No Firebase App has been created."Managing Dev and Prod Environments
A common production pattern is to maintain two separate Firebase projects — one for development and one for production — each with its own API keys, databases, and storage buckets. The FlutterFire CLI supports this via the --project flag combined with Dart build flavors:
Generating options for multiple environments
# Generate options for the dev project into a named file
flutterfire configure \
--project=my-app-dev \
--out=lib/firebase_options_dev.dart \
--yes
# Generate options for the prod project into a separate file
flutterfire configure \
--project=my-app-prod \
--out=lib/firebase_options_prod.dart \
--yes
Then select the correct options file at startup using a compile-time constant or a Dart flavor:
Switching Firebase project by flavor
// main_dev.dart — entry point for the "dev" flavor
import 'firebase_options_dev.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp(environment: 'development'));
}
// main_prod.dart — entry point for the "prod" flavor
import 'firebase_options_prod.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(const MyApp(environment: 'production'));
}
lib/firebase_options*.dart to your .gitignore if your Firebase API keys are sensitive. For open-source projects, Firebase Web API keys are not secret (they identify the project, not authenticate it — Firebase Security Rules are your real access guard), but server keys (service account JSON) should never be committed.Summary
The FlutterFire CLI is the standard, officially recommended way to connect a Flutter app to Firebase. Running flutterfire configure generates a strongly-typed DefaultFirebaseOptions class with per-platform configuration. Calling Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform) in an async main() bootstraps every Firebase service before the widget tree runs. For multi-environment setups, generate separate options files for dev and prod and select the right one via Dart flavors or compile-time entry points.