| j / k | Scroll down / up |
| gg | Scroll to top |
| G | Scroll to bottom |
| ⌘K / / | Open search |
| ? | Show this help |
| Esc | Close search / help |
| n / N | Next / previous section |
| h | Go to landing |
| d | Go to docs |
| : | Command mode — type section name |
Press ? or Esc to close
Four Firebase products, initialized once in bootstrap(). Each is wrapped by a thin static facade so feature code never touches the SDK directly.
Future<void> bootstrap() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await initializeFirebase();
// ... DI registration
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
}
Future<void> initializeFirebase() async {
AnalyticsService.init();
CrashlyticsService.initialize();
RemoteConfigService.initialize();
await NotificationService.initialize();
} | Product | Facade | Used for |
|---|---|---|
| Analytics | AnalyticsService | Screen views (called from router redirect), events |
| Crashlytics | CrashlyticsService | Fatal + non-fatal errors, caught in ErrorBoundary |
| Messaging | NotificationService | Push tokens, foreground/background messages |
| Remote Config | RemoteConfigService | Feature flags, typed config values |
FlutterError.onError and the platform dispatcher error handler are redirected to Crashlytics (skipped in dev so it doesn't pollute the console).
void initialize() {
if (AppConfig.isDev) return;
FlutterError.onError = (details) {
Crashlytics.recordError(details.exception, details.stack);
};
PlatformDispatcher.instance.onError = (error, stack) {
Crashlytics.recordError(error, stack);
return true;
};
} Keys live in remote_config_keys.dart. Access values through typed getters with a default, so a missing fetch never crashes the app.
bool get showNewFeed => getBoolWithDefault('show_new_feed', false); firebase_* outside config/utils/. Features call the facade, never the SDK.