terisuke
@terisuke (寺田 康佑)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

main.dartにおけるFirebase.initializeApp())とrunAppの書き方

解決したいこと

main.dartでFirebase.initializeApp())をbuild前に動かしたいです。
Flutter×FireBaseでTwitterのクローンアプリのようなものを作っています。
違うゾーンでWidgetsFlutterBinding.ensureInitialized()とrunAppが呼び出されていることを
ターミナルで警告され、同じゾーンに書くようにするとFirebase.initializeApp())がbuild前に呼び込まれなくなってしまいました。
ensureInitializedとrunAppを同じゾーンで呼び出し、尚且つinitializeApp()をbuild前に動かすためにはどうしたらいいか、アドバイスを頂きたいです!

発生している問題・エラー

ターミナル上に

══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞═════════════════════════════════════════════════════════
The following assertion was thrown during runApp:
Zone mismatch.
The Flutter bindings were initialized in a different zone than is now being used. This will likely
cause confusion and bugs as any zone-specific configuration will inconsistently use the
configuration of the original binding initialization zone or this zone based on hard-to-predict
factors such as which zone was active when a particular callback was set.
It is important to use the same zone when calling `ensureInitialized` on the binding as when calling
`runApp` later.
To make this warning fatal, set BindingBase.debugZoneErrorsAreFatal to true before the bindings are
initialized (i.e. as the first statement in `void main() { }`).

と出てアプリがLoadingのまま動かない

または、問題・エラーが起きている画像をここにドラッグアンドドロップ

該当するソースコード

main.dart
//dart
import 'dart:async';
// flutter
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; 
// options
import 'firebase_options.dart';
// packages
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:udemy_flutter_sns/constants/others.dart';
import 'package:sentry/sentry.dart';
// models
import 'package:udemy_flutter_sns/models/main_model.dart';
import 'package:udemy_flutter_sns/models/mute_users_model.dart';
import 'package:udemy_flutter_sns/models/sns_bottom_navigation_bar_model.dart';
import 'package:udemy_flutter_sns/models/themes_model.dart';
// constants
import 'package:udemy_flutter_sns/constants/strings.dart';
import 'package:udemy_flutter_sns/constants/themes.dart';
// components
import 'package:udemy_flutter_sns/views/main/articles_screen.dart';
import 'package:udemy_flutter_sns/views/auth/verify_email_page.dart';
import 'package:udemy_flutter_sns/details/sns_bottom_navigation_bar.dart';
import 'package:udemy_flutter_sns/views/login_page.dart';
import 'package:udemy_flutter_sns/views/main/home_screen.dart';
import 'package:udemy_flutter_sns/views/main/search_page.dart';
import 'package:udemy_flutter_sns/views/main/profile_screen.dart'; // CupertinoWidgetsのためにimport追加

Future<void> main() async {
  await runZonedGuarded(() async {
    WidgetsFlutterBinding.ensureInitialized();
    await dotenv.load(fileName: ".env");
    await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
    await Sentry.init(
      (options) {
        options.dsn = dotenv.env['SENTRY_DSN']!;
      },
      appRunner: () => runApp(const ProviderScope(child: MyApp())),
    );
  }, (error, stackTrace) {
    if (!kIsWeb && Firebase.apps.isNotEmpty) {
      FirebaseCrashlytics.instance.recordError(error, stackTrace);
    } else {
      print('Error occurred, but Firebase is not initialized');
      print('Error: $error');
      print('StackTrace: $stackTrace');
    }
  });
}

class MyApp extends ConsumerWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    if (!kIsWeb) {
      FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
    }

    final User? onceUser = FirebaseAuth.instance.currentUser;
    final ThemeModel themeModel = ref.watch(themeProvider);
    return MaterialApp(
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      debugShowCheckedModeBanner: false,
      title: appTitle,
      theme: themeModel.isDarkTheme
          ? darkThemeData(context: context)
          : lightThemeData(context: context),
      home: onceUser == null
          ? const LoginPage()
          : onceUser.emailVerified
              ? MyHomePage(
                  themeModel: themeModel,
                )
              : const VerifyEmailPage(),
    );
  }
}

class SomethingWentWrong extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Something went wrong!'),
      ),
    );
  }
}

class Loading extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: CircularProgressIndicator(),
      ),
    );
  }
}

class MyHomePage extends ConsumerWidget {
  const MyHomePage({Key? key, required this.themeModel}) : super(key: key);
  final ThemeModel themeModel;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final MainModel mainModel = ref.watch(mainProvider);
    final SNSBottomNavigationBarModel snsBottomNavigationBarModel =
        ref.watch(snsBottomNavigationBarProvider);
    return Scaffold(
      body: mainModel.isLoading
          ? Center(
              child: Text(returnL10n(context: context)!.loading),
            )
          : PageView(
              controller: snsBottomNavigationBarModel.pageController,
              onPageChanged: (index) =>
                  snsBottomNavigationBarModel.onPageChanged(index: index),
              // childrenの個数はElementsの数
              children: [
                // 注意:ページじゃないのでScaffold
                HomeScreen(
                  mainModel: mainModel,
                  themeModel: themeModel,
                  muteUsersModel: MuteUsersModel(),
                ),
                SearchPage(
                  mainModel: mainModel,
                ),
                const ArticlesScreen(),
                ProfileScreen(
                  mainModel: mainModel,
                ),
              ],
            ),
      bottomNavigationBar: SNSBottomNavigationBar(
          snsBottomNavigationBarModel: snsBottomNavigationBarModel),
    );
  }
}

自分で試したこと

Firebaseの初期化が完了するまでアプリを起動しないようにするための新しいウィジェット[BootStrap]を作成、Firebaseの初期化を待ち、完了後に[MyApp]を表示するようにした。
加えて[BootStrap]を[runZonedGuarded]に入れてrunAppと同じゾーンで呼び出すようにした。

main.dart
//dart
import 'dart:async';
// flutter
import 'package:flutter/material.dart';
// packages
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:udemy_flutter_sns/constants/others.dart';
// models
import 'package:udemy_flutter_sns/models/main_model.dart';
import 'package:udemy_flutter_sns/models/mute_users_model.dart';
import 'package:udemy_flutter_sns/models/sns_bottom_navigation_bar_model.dart';
import 'package:udemy_flutter_sns/models/themes_model.dart';
// options
import 'firebase_options.dart';
// constants
import 'package:udemy_flutter_sns/constants/strings.dart';
import 'package:udemy_flutter_sns/constants/themes.dart';
// components
import 'package:udemy_flutter_sns/views/main/articles_screen.dart';
import 'package:udemy_flutter_sns/views/auth/verify_email_page.dart';
import 'package:udemy_flutter_sns/details/sns_bottom_navigation_bar.dart';
import 'package:udemy_flutter_sns/views/login_page.dart';
import 'package:udemy_flutter_sns/views/main/home_screen.dart';
import 'package:udemy_flutter_sns/views/main/search_page.dart';
import 'package:udemy_flutter_sns/views/main/profile_screen.dart'; // CupertinoWidgetsのためにimport追加

Future<void> main() async {
  await runZonedGuarded(() async {
    WidgetsFlutterBinding.ensureInitialized();
    await dotenv.load();
    // ここでDefaultFirebaseOptions.currentPlatformを設定します。
    await Firebase.initializeApp(
        options: DefaultFirebaseOptions.currentPlatform);
    runApp(const ProviderScope(child: MyApp()));
  }, (error, stackTrace) {
    FirebaseCrashlytics.instance.recordError(error, stackTrace);
  });
}

class MyApp extends ConsumerWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // FirebaseCrashlyticsインスタンスをここで設定します。
    FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
    final User? onceUser = FirebaseAuth.instance.currentUser;
    final ThemeModel themeModel = ref.watch(themeProvider);

    // FutureBuilderは必要ないため、直接MaterialAppを返します。
    return MaterialApp(
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      debugShowCheckedModeBanner: false,
      title: appTitle,
      theme: themeModel.isDarkTheme
          ? darkThemeData(context: context)
          : lightThemeData(context: context),
      home: onceUser == null
          ? const LoginPage()
          : onceUser.emailVerified
              ? MyHomePage(
                  themeModel: themeModel,
                )
              : const VerifyEmailPage(),
    );
  }
}

class SomethingWentWrong extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text('Something went wrong!'),
      ),
    );
  }
}

class Loading extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: CircularProgressIndicator(),
      ),
    );
  }
}

class MyHomePage extends ConsumerWidget {
  const MyHomePage({Key? key, required this.themeModel}) : super(key: key);
  final ThemeModel themeModel;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    // MainModelが起動し、init()が実行されます
    final MainModel mainModel = ref.watch(mainProvider);
    final SNSBottomNavigationBarModel snsBottomNavigationBarModel =
        ref.watch(snsBottomNavigationBarProvider);
    return Scaffold(
      body: mainModel.isLoading
          ? Center(
              child: Text(returnL10n(context: context)!.loading),
            )
          : PageView(
              controller: snsBottomNavigationBarModel.pageController,
              onPageChanged: (index) =>
                  snsBottomNavigationBarModel.onPageChanged(index: index),
              // childrenの個数はElementsの数
              children: [
                // 注意:ページじゃないのでScaffold
                HomeScreen(
                  mainModel: mainModel,
                  themeModel: themeModel,
                  muteUsersModel: MuteUsersModel(),
                ),
                SearchPage(
                  mainModel: mainModel,
                ),
                const ArticlesScreen(),
                ProfileScreen(
                  mainModel: mainModel,
                ),
              ],
            ),
      bottomNavigationBar: SNSBottomNavigationBar(
          snsBottomNavigationBarModel: snsBottomNavigationBarModel),
    );
  }
}

結果
デバッグを行ったところ
104行目の

main.dart
final MainModel mainModel = ref.watch(mainProvider);

例外が発生しました
FirebaseException ([core/no-app] No Firebase App '[DEFAULT]' has been created - call Firebase.initializeApp())

というエラーが出て動かない。

よろしくお願いいたします!

0

No Answers yet.

Your answer might help someone💌