lucavenir / go_router_riverpod

An example on how to use Riverpod + GoRouter
469 stars 70 forks source link

Show splash screen during Firebase.initializeApp() #18

Closed philitell closed 1 year ago

philitell commented 1 year ago

First of all, thank you for this great project - the examples are very, very usefull!!! Great work!!

For a personal project i am using riverpod + go_router + firebase (your firebase-example helped me a lot) My question: Is there a way to display a splash-screen during the initialization procedure of firebase? (based on your firebase-example) Initialization procedure: await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

This is what i want to achieve with go_router:

// provider
final firebaseInitializerProvider = FutureProvider<FirebaseApp>((ref) async {
  logger.info("initialize firebase");
  final firebaseApp = await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );
  return firebaseApp;
});

// main
void main() async {
  runApp(const ProviderScope(child: MyApp()));
}

// main App
class MyApp extends ConsumerWidget {
  const MyApp({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final firebaseInitializer = ref.watch(firebaseInitializerProvider);
    return MaterialApp.router(
      debugShowCheckedModeBanner: false,
      title: "MyApp",
      home: firebaseInitializer.when(
        data: (data) {
          return const HomeScreen();
        },
        error: (error, stackTrace) => ErrorScreen(error: "$error"),
        loading: () => const LoadingScreen(),
      ),
    );
  }
}
lucavenir commented 1 year ago

First of all, thank you for this great project - the examples are very, very usefull!!! Great work!!

🙇‍♂️✨ Thank you for the feedback, it is very appreciated!

Is there a way to display a splash-screen during the initialization procedure of firebase

Yes, several options are available

Initialization procedure: await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);

This likely should be done before even rendering the application.

Snippet:

// WARNING: PSEUDOCODE
Future<void> main() async {
  runApp(LoadingApp());

  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(...);

  runApp(YourActualApp());
}

This is actually pretty common when loading singletons (e.g. Firebase) that shouldn't be exposed through Riverpod. There's plenty of in-depth tutorials if you look carefully.

Finally, I want to underline that your firebaseInitializerProvider strategy is indeed very viable for... literally everything else.

Quick example (I use codegen - I hope it's still readable):

@riverpod
FutureOr<PackageInfo> packageInfo(PackageInfoRef ref) async {
  final result = PackageInfo.fromPlatform();
  ref.keepAlive();
  return result;
}

@riverpod
FutureOr<SharedPreferences> sharedPreferences(SharedPreferencesRef ref) async {
  final result = await SharedPreferences.getInstance();
  ref.keepAlive();
  return result;
}

@riverpod
FutureOr<void> splashLoading(SplashLoadingRef ref) async {
  await Future.wait([
    ref.watch(packageInfoProvider.future),
    ref.watch(sharedPreferencesProvider.future),
  ]);
}

splashLoadingProvider is used inside a LoadingPage so that everything can be asynchronously loaded; then, whenever I need, say, sharedPreferences, I just use .requireValue since that won't reload, ever.