First step in using Firebase within Flutter app is to perform initialization.
I've tried doing this within main() and it works
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
I've utilized Riverpod for state management - with Provider for firebase instance and access to the class with connection methods (Signin, Signout...) and StreamProvider for user state. Again this works fine - recognizing when the user has signed in or signed out - rebuilding widgets and redirecting to proper screen...
Now I would like to also move the code pasted above from main() into a provider definition and have shown proper screen depending on the status of firebase initialization.
I've seen official Firebase example suggesting to use FutureBuilder for this - however, since I've used Riverpod - my idea was to use FutureProvider
for initializing firebase. However, whatever I try the app keeps crashing with some null exceptions.
If anybody can share their example of firebase.initializeApp()
via FutureProvider it would be great.
I do it like this: (Can be done via singleton if needed)
// services_initialization.dart
class ServiceInit{
ServiceInit(ProviderContainer container) {
_container = container;
}
// The container is needed to access the ref and load other providers, if required
late final ProviderContainer _container;
Future<void> init() async {
await _initFirebase();
// initialization of other services, example:
await _container.read(somethingFutureProvider.future);
}
Future<void> _initFirebase() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
}
}
The main file looks like this:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final _container = ProviderContainer();
await ServiceInit(_container).init();
runApp(UncontrolledProviderScope(
container: _container,
child: WeatherMain(),
),
}
I've found a solution for this so pasting it bellow. Big thanks to 2002Bishwajeet who posted a Firebase & Riverpod authentication example covering this topic as well at GitHub
The idea is to create FutureProvider like this:
final firebaseInitProvider = FutureProvider<FirebaseApp>((ref) async
{
return await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
});
In the main() keep just WidgetsFlutterBinding.ensureInitialized()
Like this:
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(ProviderScope(child: const MyApp()));
}
Then watch the provider and use.when() to show the correct screen - basically to move to AuthenticationWrapper widget only once the Firebase.initializeApp() has completed the initialization process.
Like this:
class MyApp extends ConsumerWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final _initializeFirebase = ref.watch(firebaseInitProvider);
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Firebase Authentication App',
theme: CommonTheme().mainThemeData,
home: _initializeFirebase.when(
data: (data) => const AuthenticationWrapper(),
loading: () => const ProgressScreen(),
error: (error, stackTrace) => ErrorScreen(error.toString()),
),
);
}
}
And finally the AuthenticationWrapper is handling display of SignIn or Home screen - depending on the status of the user (already signed in at Firebase Auth or not).
Like this:
class AuthenticationWrapper extends ConsumerWidget {
const AuthenticationWrapper({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final _authState = ref.watch(authStateProvider);
return _authState.when(
data: (value) {
if (value != null) {
return HomeScreen();
} else {
return LoginScreen();
}
},
error: ((error, stackTrace) => ErrorScreen(error.toString())), //TO-DO set user oriented error messages
loading: (() => ProgressScreen()),
);
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.