skip to content

How do you gate firebase_analytics and firebase_crashlytics collection in a Flutter app on user consent?

level: middleimportance: nice to knowfreq 22%

answer

  1. setAnalyticsCollectionEnabled persists
  2. setConsent: analytics vs ad storage
  3. three setConsent flags are web-only
  4. setCrashlyticsCollectionEnabled persists too
  5. check, send or delete unsent reports

basics

~10 s

Use FirebaseAnalytics.instance.setConsent(...) and setAnalyticsCollectionEnabled for Analytics, and FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled for crash reports; both enable flags persist, and Crashlytics keeps opted-out reports on the device to send or delete later.

solid answer

~40 s

Analytics has two levers. `setAnalyticsCollectionEnabled(false)` stops collection on this device; it is enabled by default, and the setting persists across sessions. `setConsent(...)` sets consent-mode signals: `analyticsStorageConsentGranted`, `adStorageConsentGranted`, `adUserDataConsentGranted` and `adPersonalizationSignalsConsentGranted` on Android, iOS and web, plus three storage flags that apply only to the web. With analytics storage denied, `getSessionId()` returns `null`, and `appInstanceId` returns `null` when consent is denied. Crashlytics has `setCrashlyticsCollectionEnabled(bool)`, which is persisted and overrides the manifest, plist and Firebase-wide settings. When collection is off, reports stay on the device: `checkForUnsentReports()`, `sendUnsentReports()` and `deleteUnsentReports()` implement opt-in. Apply the user's choice at startup, before logging, and default to off where consent is required.

code

dart · 20 lines
dart
import 'package:firebase_analytics/firebase_analytics.dart';
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
import 'package:flutter/foundation.dart';

Future<void> applyConsent({required bool analytics, required bool crashes}) async {
  final fa = FirebaseAnalytics.instance;
  await fa.setConsent(
    analyticsStorageConsentGranted: analytics,
    adStorageConsentGranted: false,
    adUserDataConsentGranted: false,
    adPersonalizationSignalsConsentGranted: false,
  );
  await fa.setAnalyticsCollectionEnabled(analytics);

  final fc = FirebaseCrashlytics.instance;
  await fc.setCrashlyticsCollectionEnabled(crashes && !kDebugMode);
  if (!crashes && await fc.checkForUnsentReports()) {
    await fc.deleteUnsentReports();
  }
}

go deeper

for a junior

Know the two switches, setAnalyticsCollectionEnabled and setCrashlyticsCollectionEnabled, and that both settings persist across launches.

for a middle

Explain setConsent's flags and which are web-only, what returns null when analytics storage is denied, and the Crashlytics unsent-report methods.

for a senior

Design a consent flow that applies the stored choice before the first event, sets native defaults where required, and handles withdrawal and pending crash reports.

for a principal

Balance data needs against regional consent rules across platforms, and make the consent state a single source of truth that every SDK in the app obeys.

## Why consent is a client concern In many markets, analytics and crash reporting need the user's permission before data leaves the device. The FlutterFire plugins give you the switches, and your app decides when to flip them. For a language-learning app, the flow might be: 1. Show a consent screen on first launch. 2. Store the choice. 3. Apply it at every startup before any event or crash report is sent. The legal question of what needs consent is outside this topic. The API is the subject here. ## firebase_analytics: two different levers | API | Effect | Default | Persisted | |---|---|---|---| | `setAnalyticsCollectionEnabled(bool)` | turns Analytics collection on or off for this device | enabled | yes, across sessions | | `setConsent({...})` | sets consent-mode signals that govern storage and ad use | set by platform config | not stated by the plugin | `setConsent` takes optional `bool` flags: - **Android, iOS and web:** `analyticsStorageConsentGranted`, `adStorageConsentGranted`, `adUserDataConsentGranted`, `adPersonalizationSignalsConsentGranted`. - **Web only:** `functionalityStorageConsentGranted`, `personalizationStorageConsentGranted`, `securityStorageConsentGranted`. Some effects are visible from Dart: - `getSessionId()` returns `null` if `analyticsStorageConsentGranted` is `false` or the session has expired. - `appInstanceId` returns `null` if consent has been denied. Default consent states, before your code runs, are set per platform in native configuration. The API docs link Google's guides for iOS, Android and web. If consent must be off until the user agrees, set those defaults natively. A Dart call made after startup cannot cover events logged before it runs. ## firebase_crashlytics: collection and unsent reports `FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(bool)`: - It **persists** across app runs. - It **overrides** the Android manifest, iOS plist and Firebase-wide automatic collection settings. - `isCrashlyticsCollectionEnabled` reads the current state. When collection is disabled, crash reports are still **captured and kept on the device**. That enables an opt-in flow: 1. `checkForUnsentReports()` returns `true` if reports are waiting. With automatic collection on, it always returns `false`, because reports upload automatically. 2. If the user agrees, `sendUnsentReports()` uploads them. 3. If the user declines, `deleteUnsentReports()` discards them. `didCrashOnPreviousExecution()` tells you the last run crashed. That is a natural moment to ask whether to send the report. ## Putting it together - Keep the consent state in your own storage, such as a preferences entry, and apply it in `main()` after `Firebase.initializeApp` and before `runApp`. - Apply both plugins from one function, so they never disagree. - In debug builds, many teams disable Crashlytics collection so development crashes never reach the console. - Clear identifiers if the user withdraws consent: `setUserId(id: null)` for Analytics and `setUserIdentifier('')` for Crashlytics. `resetAnalyticsData()` clears this device's analytics data. ## What not to assume - Disabling collection does not erase data already sent. Deleting server-side data is a separate process. - Consent flags do not stop your own `logEvent` calls from running. They change how the SDK stores and uses the data. Use `setAnalyticsCollectionEnabled(false)` when nothing should be collected at all.

  • Crash collection is off until the user opts in, and the app crashed yesterday. How do you offer to send that report?
    With collection disabled, Crashlytics keeps reports on the device. At the next launch, `didCrashOnPreviousExecution()` or `checkForUnsentReports()` tells you something is waiting. Ask the user, then call `sendUnsentReports()` to upload or `deleteUnsentReports()` to discard.
  • Why is calling setAnalyticsCollectionEnabled(false) in main() not always enough for a consent-first app?
    The native SDK can start collecting automatic events as soon as it initialises, before your Dart code runs. Default consent and collection states can be set in native configuration, as the API docs' linked platform guides describe. The Dart call then only updates the state once the user decides.

saying these in an interview costs you the question

  • setConsent flags all behave the same on Android, iOS and web.
  • setAnalyticsCollectionEnabled resets to enabled on every launch.
  • With Crashlytics collection off, crashes are not captured at all.
  • Disabling collection deletes data already sent to Firebase.
  • checkForUnsentReports is useful even with automatic collection on.