Home > OS >  Provider at root dependent on firebase fetches value before Firebase.initializeApp() completes
Provider at root dependent on firebase fetches value before Firebase.initializeApp() completes

Time:02-16

So, I'm building an app with MultiProviders that are on the root of the application above MaterialApp() (So that the value is available in all of the sub-widgets and subsequent pages that I navigate to also).

For my main function:

void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
   }

Where as the MyApp class is:

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  DarkThemeProvider themeChangeProvider = DarkThemeProvider();
  final Future<FirebaseApp> _firebaseApp = Firebase.initializeApp();

  void getCurrentAppTheme() async {
    themeChangeProvider.darkTheme =
    await themeChangeProvider.darkThemePreference.getTheme();
  }

  @override
  void initState() {
    super.initState();
    getCurrentAppTheme();
  }

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (_) => themeChangeProvider,
        ),
        ChangeNotifierProvider(
          create: (_) => VideoPlayService.instance(),
        ),
        ChangeNotifierProvider(
          create: (_) => FirestoreAPI.instance(),
        ),
        ChangeNotifierProvider(
          create: (_) => AuthUtil.instance(),
        ),
        ChangeNotifierProvider(
          create: (_) => PageSwitcher.instance(),
        ),
      ],
      child: Consumer5(
        builder: (
          ctx,
          DarkThemeProvider darkThemeProvider,
          VideoPlayService videoPlayService,
          FirestoreAPI firestoreAPI,
          AuthUtil user,
          PageSwitcher switcher,
          child,
        ) {
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            themeMode: Provider.of<DarkThemeProvider>(ctx).getCurrentTheme,
            theme: DarkThemeProvider.customLightTheme,
            darkTheme: DarkThemeProvider.customDarkTheme,
            home: FutureBuilder(
              future: _firebaseApp,
              builder: (cx, snapshot) {
                if (snapshot.hasError) {
                  return const Center(
                    child: Text('Could not connect to firebase'),
                  );
                } else if (snapshot.connectionState == ConnectionState.done) {
                  return const Wrapper();
                } else {
                  return const SplashScreen();
                }
              },
            ),
          );
        },
      ),
    );
  }
}

I've used future builder to initialize the app with Firebase but the issue is the provider AuthUtil and FirestoreAPI are both dependent on firebase, now if I were to remove these both and add them as a child to the Future builder(Above Wrapper) I would lose the access to the provider in my whole app (Since the provider will not be at the root anymore, It will only be available for the first child only and whenever I'll navigate to another page it will not be available).

And in the given code as I've written the firebase is not initialized but the Provider's instance is created first hence throwing an exception that firebase was not initialized, I also need to be able to show the SplashScreen while Firebase is initializing, I tried to wrap my head around future providers but I'm not sure how they work or even, if they are relevant to my case.

In summary I want to initialize the firebase first, then create the providers of AuthUtil and FirestoreAPI class while maintaining the scheme of showing splash screen or Error screen if firebase initialization fails and keeping the providers at the root (for the record I did try making a FutureBuilder at root but the It didn't make sense and I had to have multiple MaterialApp callings for the snapshot's cases which is just nonsense).

If anybody can help me solve this as soon as possible...? It would be a great save.

CodePudding user response:

I typically call initializeApp in my main method:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(const MyApp());
}

While the initializeApp call is asynchronous, it is really just a call to the underlying native SDK where it is synchronous. So we're talking about waiting for nano-seconds here, not tens to hundreds of milliseconds where a FutureBuilder would be meaningful.

  • Related