feat(observability): add Firebase Crashlytics with opt-in toggle in settings#32
feat(observability): add Firebase Crashlytics with opt-in toggle in settings#32using-system merged 17 commits intomainfrom
Conversation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mport Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…and section Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds opt-in Firebase Crashlytics integration to the app, exposing a new “Observability” settings section with a crash reporting toggle and persisting the preference via SharedPreferences.
Changes:
- Add Firebase Crashlytics + Firebase Core dependencies and Android Gradle plugin wiring.
- Add an Observability settings section with a Crashlytics on/off toggle backed by Riverpod + SharedPreferences.
- Add i18n strings and unit/widget tests for the new storage and UI.
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
pubspec.yaml |
Adds firebase_core and firebase_crashlytics dependencies. |
pubspec.lock |
Locks Firebase/Crashlytics transitive dependencies. |
lib/main.dart |
Initializes Firebase/Crashlytics at startup and conditionally wraps runApp in a guarded zone. |
lib/features/settings/presentation/settings_page.dart |
Inserts the Observability section between General and Actions. |
lib/features/observability/data/crashlytics_preference_storage.dart |
Implements SharedPreferences read/write for the Crashlytics opt-in boolean. |
lib/features/observability/providers.dart |
Adds Riverpod providers/notifier to read/write preference and call Crashlytics SDK. |
lib/features/observability/presentation/crashlytics_toggle_tile.dart |
Adds the SwitchListTile UI + error snackbar on write failures. |
lib/features/observability/presentation/observability_section.dart |
Groups observability settings tiles for the Settings page. |
lib/l10n/app_en.arb |
Adds EN strings (and metadata) for Observability + Crashlytics toggle. |
lib/l10n/app_fr.arb |
Adds FR strings for Observability + Crashlytics toggle. |
lib/l10n/app_de.arb |
Adds DE strings for Observability + Crashlytics toggle. |
lib/l10n/app_es.arb |
Adds ES strings for Observability + Crashlytics toggle. |
android/settings.gradle.kts |
Adds Google Services + Crashlytics Gradle plugins (pluginManagement). |
android/app/build.gradle.kts |
Applies Google Services + Crashlytics plugins to the Android app module. |
android/app/google-services.json |
Adds Firebase Android config (currently committed). |
SPEC.md |
Documents Crashlytics in the tech stack under Observability. |
docs/superpowers/specs/2026-04-21-firebase-crashlytics-design.md |
Design doc for Crashlytics integration and toggle behavior. |
docs/superpowers/plans/2026-04-21-firebase-crashlytics.md |
Step-by-step implementation plan for the feature. |
test/features/observability/data/crashlytics_preference_storage_test.dart |
Tests for storage default/overwrite/corruption handling. |
test/features/observability/presentation/crashlytics_toggle_tile_test.dart |
Widget tests for toggle UI and persistence. |
test/features/observability/presentation/observability_section_test.dart |
Widget tests ensuring section contains tile and divider. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…one mismatch Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d Crashlytics calls The catch block now captures and logs the stack trace for easier diagnosis. The provider guards setCrashlyticsCollectionEnabled behind Firebase.apps.isNotEmpty so the toggle works as a no-op on platforms without Firebase config. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 21 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| (error, stack) { | ||
| FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); |
There was a problem hiding this comment.
The zone error handler calls FirebaseCrashlytics.instance.recordError(...) unconditionally. If Firebase initialization was skipped/failed in the try block (e.g., missing config on iOS), this will likely throw ("no default Firebase app") and can cascade into additional unhandled errors. Guard this call behind a Firebase-initialized check (e.g., Firebase.apps.isNotEmpty) and/or wrap it in a try/catch with a fallback logger so the app remains stable when Firebase is unavailable.
| (error, stack) { | |
| FirebaseCrashlytics.instance.recordError(error, stack, fatal: true); | |
| (error, stack) async { | |
| if (Firebase.apps.isEmpty) { | |
| debugPrint('Unhandled zone error (Firebase unavailable): $error\n$stack'); | |
| return; | |
| } | |
| try { | |
| await FirebaseCrashlytics.instance | |
| .recordError(error, stack, fatal: true); | |
| } catch (e, crashlyticsStack) { | |
| debugPrint( | |
| 'Failed to report error to Crashlytics: $e\n$crashlyticsStack\n' | |
| 'Original error: $error\n$stack', | |
| ); | |
| } |
| ``` | ||
| lib/features/observability/ | ||
| ├── data/ | ||
| │ └── observability_storage.dart # SharedPreferences wrapper | ||
| ├── domain/ | ||
| │ └── crashlytics_preference.dart # Boolean preference type | ||
| ├── presentation/ | ||
| │ ├── observability_section.dart # Section widget for settings page | ||
| │ └── crashlytics_toggle_tile.dart # SwitchListTile for Crashlytics | ||
| └── providers.dart # Riverpod AsyncNotifier + provider | ||
| ``` |
There was a problem hiding this comment.
This spec’s module file map no longer matches the implemented code: it references observability_storage.dart and a domain/ preference type, but the PR introduces data/crashlytics_preference_storage.dart (and no domain/ layer). Please update the file map to reflect the actual paths/structure so future contributors don’t follow stale guidance.
| ## Prerequisites (manual) | ||
|
|
||
| The user must place the Firebase config files (these are gitignored and cannot be generated by code): | ||
| - `android/app/google-services.json` — download from Firebase Console | ||
| - `ios/Runner/GoogleService-Info.plist` — download from Firebase Console | ||
|
|
There was a problem hiding this comment.
This plan states Firebase config files are gitignored and must be placed manually, but the PR also adds android/app/google-services.json to version control. Either update the plan to match the new approach, or (preferably) keep the config files out of the repo by gitignoring them and removing committed copies.
Summary
firebase_core,firebase_crashlytics) with Android Gradle plugin configurationTest plan
setCrashlyticsCollectionEnabled(true)calledflutter test test/features/observability/→ 11 tests passflutter analyze→ no new issues🤖 Generated with Claude Code