Firebaseを初期化した際、次のエラーが表示されることがあります。
FirebaseApp with name [DEFAULT] already exists
Flutter:
[core/duplicate-app]
A Firebase App named "[DEFAULT]" already exists
すでに存在するFirebaseAppと同じ名前で、もう一度新しいFirebaseAppを作成しようとしています。
初期化と取得の違い
新しいFirebaseAppを作成
Android:
FirebaseApp.initializeApp(context, options)
Flutter:
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
作成済みのFirebaseAppを取得
Android:
FirebaseApp.getInstance()
Flutter:
final app = Firebase.app();
既存のデフォルトAppを使うだけなら、再初期化しません。
Androidの原因1:自動初期化と手動初期化
一般的なAndroidアプリでは、FirebaseInitProviderが[DEFAULT]を自動初期化します。
その後、Applicationで次を実行すると二重初期化になる場合があります。
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this, options)
}
}
標準構成なら手動初期化を削除し、既存Appを取得します。
val app = FirebaseApp.getInstance()
Androidの原因2:複数の場所で初期化
プロジェクト全体で検索します。
FirebaseApp.initializeApp
FirebaseOptions.Builder
[DEFAULT]
確認する場所:
- Application
- Activity
- Service
- ContentProvider
- DI Module
- StartupManager
- flavor固有コード
作成処理を一か所へ集約します。
Flutterの原因1:main以外でも初期化
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
await FirebaseService.initialize();
runApp(const MyApp());
}
FirebaseService.initialize()内でも同じ初期化をしていると重複します。
サービスクラスでは既存インスタンスを使います。
class FirebaseService {
FirebaseApp get app => Firebase.app();
FirebaseAuth get auth => FirebaseAuth.instance;
}
Flutterの原因2:Widgetのbuild内
build()は複数回呼ばれます。
避けたい例:
Widget build(BuildContext context) {
Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
return const HomePage();
}
初期化はmain()など、一度だけ通る起動処理へ置きます。
Flutterの原因3:Providerの再生成
ProviderやRiverpodの生成処理で初期化すると、ライフサイクルによって再実行される場合があります。
起動時に初期化したAppをProviderへ渡します。
final app = await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(
ProviderScope(
overrides: [firebaseAppProvider.overrideWithValue(app)],
child: const MyApp(),
),
);
原因4:複数のmain・bootstrap
main_development.dart
main_production.dart
bootstrap.dart
各mainと共通bootstrapの両方で初期化していないか確認します。
Future<void> bootstrap(FirebaseOptions options) async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: options);
runApp(const MyApp());
}
FirebaseAppを作る責任をbootstrapだけへ集約します。
原因5:複数プロジェクトへ同じ名前を使う
await Firebase.initializeApp(
options: primaryOptions,
);
await Firebase.initializeApp(
options: secondaryOptions,
);
どちらも名前を指定していないため、[DEFAULT]を使用します。
2つ目には固有名を付けます。
final secondary = await Firebase.initializeApp(
name: 'secondary',
options: secondaryOptions,
);
対象サービスも名前付きAppから取得します。
final auth = FirebaseAuth.instanceFor(app: secondary);
原因6:Flutter Webの古い手動設定
Dart側のFirebase.initializeApp()に加えて、web/index.htmlでJavaScriptの初期化コードが残っていないか確認します。
<script>
firebase.initializeApp(firebaseConfig);
</script>
現在のFlutterFire構成では、通常Dart側の初期化へ統一します。
Hot Restart後だけ発生する場合
Hot RestartはDartの起動処理を再実行します。Full Restartで一時的に解消しても、初期化経路が複数ある場合は再発します。
initializeAppを全体検索- ネイティブ側の手動初期化を確認
- Web JavaScriptを確認
- 同じ名前で異なるoptionsを使っていないか確認
テスト環境で発生する場合
テストごとに同じ名前で作成している可能性があります。
late FirebaseApp testApp;
setUpAll(() async {
testApp = await Firebase.initializeApp(
name: 'test-app',
options: testOptions,
);
});
tearDownAll(() async {
await testApp.delete();
});
製品用の[DEFAULT]ではなく、テスト専用の名前を使います。
登録済みAppを確認する
Android:
FirebaseApp.getApps(context).forEach {
Log.d("Firebase", "${it.name}: ${it.options.projectId}")
}
Flutter:
for (final app in Firebase.apps) {
debugPrint('${app.name}: ${app.options.projectId}');
}
同名Appが存在することを確認したら、作成ではなく取得へ変更します。
エラーをcatchして無視しない
try {
await Firebase.initializeApp();
} catch (_) {}
この方法は、構成不備など別のエラーも隠します。重複している初期化処理を特定してください。
まとめ
duplicate-appを直す基本は次のとおりです。
initializeAppを全体検索- Androidの自動初期化と手動初期化を併用しない
- Flutterではmainまたはbootstrapで一度だけ初期化
- WidgetやProviderの再生成で初期化しない
- 2つ目以降のFirebaseAppへ固有名を付ける
- 既存Appは
getInstance()またはFirebase.app()で取得
逆に[DEFAULT]が存在しない場合はDefault FirebaseApp is not initializedを確認してください。
一次情報
公式参考資料
Google PlayやFirebaseの画面・要件は変更される場合があります。公開作業の前に、リンク先の最新情報も確認してください。