In Flutter's firebase_messaging, what does requestPermission() do on iOS and on Android 13+, and how do you read its result?
answer
- returns NotificationSettings
- authorizationStatus enum
- alert, badge, sound default true
- provisional: quiet delivery on iOS
- Android 13 POST_NOTIFICATIONS prompt
basics
~10 sFirebaseMessaging.instance.requestPermission() shows the notification prompt on iOS, macOS, web and Android 13+, and returns NotificationSettings whose authorizationStatus is authorized, denied, notDetermined, provisional or deniedPermanently.
solid answer
~40 s`FirebaseMessaging.instance.requestPermission()` asks the platform for permission to show notifications and returns a `NotificationSettings`. On iOS it shows the system dialog; named flags choose what you ask for, with `alert`, `badge` and `sound` defaulting to `true` and `provisional`, `criticalAlert`, `announcement` and `carPlay` defaulting to `false`. On Android 13+ it requests `POST_NOTIFICATIONS`, which the plugin's manifest already declares. Below Android 13 it shows nothing and returns `authorized` unless the user disabled notifications in settings. You read `settings.authorizationStatus`: `authorized`, `denied`, `notDetermined`, `provisional` (iOS quiet delivery) or `deniedPermanently` (Android 13+, where the OS will not prompt again). `getNotificationSettings()` reads the same status without prompting, and `getToken()` never asks for permission on Android or Apple platforms.
code
dart · 16 linesimport 'package:firebase_messaging/firebase_messaging.dart';
Future<bool> askForShippingAlerts() async {
final messaging = FirebaseMessaging.instance;
final current = await messaging.getNotificationSettings();
if (current.authorizationStatus == AuthorizationStatus.authorized) return true;
// alert, badge and sound default to true.
final settings = await messaging.requestPermission();
return switch (settings.authorizationStatus) {
AuthorizationStatus.authorized || AuthorizationStatus.provisional => true,
AuthorizationStatus.denied ||
AuthorizationStatus.deniedPermanently ||
AuthorizationStatus.notDetermined => false,
};
}go deeper
Know that requestPermission() returns NotificationSettings, that authorizationStatus holds the decision, and that alert, badge and sound are requested by default.
Explain the platform split: a one-time iOS dialog, provisional quiet delivery, no prompt below Android 13, POST_NOTIFICATIONS from 13, and deniedPermanently meaning only settings can fix it.
Show when you ask, how you handle a permanent denial without nagging, and why token registration and permission are separate steps you can order independently.
Weigh opt-in rate against user trust: provisional delivery, contextual prompts and in-app preference screens, and how the permission strategy shapes the value of the whole push channel.
## What the method is **firebase_messaging** is the FlutterFire plugin for Firebase Cloud Messaging. The FlutterFire guide says that on iOS, macOS, web and Android 13 (API 33) or newer you must ask the user's permission before FCM payloads can be received and shown. `FirebaseMessaging.instance.requestPermission()` is the plugin's single cross-platform call for that. It returns a `Future<NotificationSettings>`. ## The named flags and their defaults Most flags only matter on Apple platforms. Their defaults in the current source: | Flag | Default | Platform | Meaning | |---|---|---|---| | `alert` | `true` | iOS/macOS | show banners and alerts | | `badge` | `true` | iOS/macOS | update the app icon badge | | `sound` | `true` | iOS/macOS | play a sound | | `provisional` | `false` | iOS | quiet, provisional authorization | | `criticalAlert` | `false` | iOS | alerts that bypass mute (needs App Store justification) | | `announcement` | `false` | iOS | Siri reads messages over AirPods | | `carPlay` | `false` | iOS | show in CarPlay | | `providesAppNotificationSettings` | `false` | iOS/macOS | an in-app settings button in system settings | Calling `requestPermission()` with no arguments therefore asks for alerts, badges and sounds. ## What happens on each platform - **iOS**: the system dialog appears. iOS shows it only once per install; later calls return the stored decision without a prompt, and a user who declined must change it in Settings. - **iOS with `provisional: true`**: no dialog. Permission is granted quietly, notifications are delivered without interrupting, and the user can later keep them quiet, promote them or turn them off. The status is `provisional`. - **macOS**: a system notification asks for permission. - **Web**: the browser's own permission prompt runs. - **Android 12 and lower**: no prompt exists. The call returns `authorized` unless the user turned notifications off for the app in system settings. - **Android 13+**: the plugin requests the `POST_NOTIFICATIONS` runtime permission. You do not add it yourself: the plugin's own `AndroidManifest.xml` declares it. ## Reading the result `NotificationSettings.authorizationStatus` is an `AuthorizationStatus` enum: 1. `authorized`: the user granted permission. 2. `denied`: permission is not granted. On Android 13+ this means the user declined, but the OS may still show another prompt. 3. `notDetermined`: the user has not chosen yet, usually before the first request. 4. `provisional`: iOS quiet authorization. 5. `deniedPermanently`: Android 13+ only. The OS will not show the prompt again, so only system settings can change it. Apple platforms report permanent denial as `denied`. The Android plugin tells these apart by combining the platform's rationale check with a stored flag that records whether it already prompted. The upstream guide text still says Android 13 cannot distinguish undetermined from denied, but the current source does. The other `NotificationSettings` fields (`alert`, `badge`, `sound` and so on) report whether each Apple setting is enabled, disabled or not supported. ## Practical rules - **Ask at a meaningful moment.** For example, ask right after the first order is placed, when shipping alerts obviously help. Because iOS asks only once, a prompt at first launch that gets declined is hard to undo. - **Check before asking.** `getNotificationSettings()` returns the current status without prompting. - **Tokens are separate.** On Android and Apple platforms `getToken()` does not request permission, so you can register the device before asking. On web, `getToken()` triggers the browser prompt if permission is not yet granted. - **Scope.** How Android runtime permissions work in general, and packages that request arbitrary permissions, belong to the app-lifecycle and permissions topic. This method covers only notifications through FCM.
- What should the app do when authorizationStatus is deniedPermanently on Android 13+?Stop calling `requestPermission()` expecting a dialog, because the OS will not show one. Explain the benefit in your own UI and offer a way to open the app's notification settings, where only the user can turn it back on. On Apple platforms the same situation reports `denied`.
- Why might a team request provisional permission on iOS instead of the full prompt?`requestPermission(provisional: true)` grants quiet delivery without interrupting the user with a dialog. The user sees real notifications in Notification Center first and can then keep them quiet, promote them or turn them off. This avoids burning the one-time prompt before the user knows the notifications are useful.
saying these in an interview costs you the question
- requestPermission shows a dialog on every Android version.
- You must add POST_NOTIFICATIONS to the app manifest yourself for firebase_messaging.
- Calling requestPermission again on iOS re-shows the dialog after a denial.
- getToken fails on Android until notification permission is granted.
- provisional: true on iOS shows the dialog with an extra button.