baaba_permission_handler 1.1.0
baaba_permission_handler: ^1.1.0 copied to clipboard
Complete permission flow for Android and iOS: request, rationale, denied and permanently denied handling, Settings guidance dialogs. Fully customizable.
1.1.0 #
Fixed #
- A prompt always finishes.
- If the app tears down its navigator while a prompt is open (a logout that rebuilds
MaterialApp, say), the prompt now counts as dismissed. Before, the request never completed, and every later request queued behind it hung too. - A snack-bar prompt no longer waits behind the app's own snack bar, which can stay up indefinitely. It becomes a dialog instead, and also while a screen reader is on.
- The snack bar has a safety timeout as well.
- If the app tears down its navigator while a prompt is open (a logout that rebuilds
- A request started from an observer callback now queues. It used to slip past the queue as if the running flow had made it, so two Android requests could run at once.
- iOS: refusing while-in-use location no longer blocks the "Always" upgrade for good. Once while-in-use is turned on in Settings, the upgrade prompt is shown.
- iOS: switching Location Services back on from the service dialog now leads to the location prompt, when the permission had never been asked for.
- A permanent denial counts for every capability that shares the native permission. After
photosis refused on Android 12, acheck(videos)(also storage there) reportspermanentlyDenied, instead of a rationale followed by an instant refusal. requestManysends the user to Settings once. With retries used up, the denied dialog now covers the permanently denied permissions too, instead of a second Settings dialog and a second trip.- One misconfigured permission no longer fails its whole batch. iOS fails a request when one permission in it lacks its usage description. The other permissions are now asked on their own.
- Per-call
options.textswins overtextsBuilder, as documented, and merges into the app's texts field by field instead of replacing them. - Button labels have their placeholders filled, in the dialogs and in
PermissionGate. PermissionGateruns one flow for a double tap, not two back to back.- Theme values that were ignored now apply. The Cupertino dialog takes
theme.barrierColorand start-aligns its steps. The snack bar takesbackgroundColor,paddingandaccentColor, and its button now followstextDirectionlike its text. clearHistorywaits for history writes still in flight, so a write can no longer land after the clear.- iOS:
locationAlwaysafterlocationnow shows the "Allow Always" prompt. Once while-in-use is granted, iOS reports background location as permanently denied even though it was never asked for. The flow took that at face value and sent the user to Settings without ever showing the upgrade prompt. It now asks once, and treats it as permanently denied only after a refusal. - iOS: Location Services switched off is reported as
serviceDisabled. iOS reports location as denied while Location Services are off globally. The flow showed "permanently denied" and pointed the user at the app's permission instead of the service. - A snack bar no longer holds up the flow. In current Flutter, a snack bar with an action stays on screen until the user acts on it, so
PermissionDialogTheme.snackBarDurationwas ignored and every later request queued behind it. It now times out and counts as dismissed. A snack bar is also no longer shown when there is noScaffoldto show it in, since in release builds it would never appear and the request would never finish; a dialog is used instead. - A
contextwith noNavigatorabove it no longer blocks the request. Passing, for example, the context of the widget that buildsMaterialAppused to fail silently: the rationale counted as "Not now", and the permission was never requested. The flow now falls back to the app's root navigator. - A dialog that throws is no longer counted as "Not now". If the presentation throws (a broken
presenterordialogBuilder, say), the error goes toPermissionObserver.onErrorand the flow carries on as if no UI was available. A rationale proceeds to the system prompt, and a follow-up dialog is skipped without settingdeclinedByUser. - Notifications below Android 13. There is no runtime permission there, and the status only mirrors the app's notification switch. When notifications are off,
notificationis now reported aspermanentlyDeniedand goes straight to the Settings dialog. Previously the flow offered an "Allow" retry that could never show a prompt, and debug builds blamed a missing manifest entry. - Background permissions are requested on their own on Android. Android 11+ ignores a background-location request bundled with other permissions.
requestMany([AppPermission.locationAlways, AppPermission.sensorsAlways])now makes a separate call for each background permission. recheckAfterSettings: falsenow also applies to the service-disabled dialog. That step used to re-check straight away, before the user could change anything.openSettingsAndRecheckresults are published. They now reach theresultsstream andPermissionObserver.onResult, soPermissionGateandwatch()update too.openSettingsAndRecheckalways waits for the return from Settings. WithrecheckAfterSettings: falseit used to re-check at once and report the state from before. Its results now carryinitialState, sochangedtells whether the user changed anything.PermissionGatewith a mix of restricted and denied permissions now requests the denied ones. It used to offer only "Open Settings".BaabaPermissions.initcalled a second time now closes the replaced instance'sresultsstream. APermissionGatewithout its ownhandlermoves over to the new instance, instead of listening to the old one.PermissionGatekeeps its own request's details. Its request was followed by a redundant re-check, soonChangedfired twice and the final results lostrequested,declinedByUserandopenedSettings. A result from a request made elsewhere is now applied directly too, without a re-check.PermissionGateignores app resumes during its own request or Settings trip. The system prompt pauses and resumes the app, and the re-check that triggered could briefly replace the request's result.PermissionGatere-checks when itshandlerchanges.watch()no longer emits a stale state when a check is overtaken by a newer result while it runs.- Bottom sheets open on the root navigator, like the dialogs, even when the
contextpassed is inside a nested navigator. - History writes are serialized. A flow now settles several permissions in parallel, and a
PermissionStorethat rewrites one shared record could have lost updates. PermissionGate's fallback uses the gate'soptions. Per-calltexts,themeandtextDirectionnow apply to the built-inPermissionRequiredView, not only to the dialogs.- Closing a prompt never closes another route. If the app pushed a route on top of an open dialog or sheet, answering the prompt now removes the prompt's own route instead of popping the route on top.
Changed #
-
The snack bar's button is now part of its content, not a
SnackBarAction, so it can followtextDirection. It looks much the same. -
Internal restructuring, with no API change: the flow's per-call state, dialog plumbing, dialog data, wording resolution and diagnostics moved out of
baaba_permissions.dartinto their own files. The dialogs andPermissionRequiredViewnow resolve wording through the same code. Independent platform calls within a flow (rationale checks, re-checks after Settings, service checks) now run in parallel. -
Example app: the branded
dialogBuildernow works in every presentation, and an on-device integration test (example/integration_test/, driven over adb byexample/tool/device_driver.py) runs the flow against a real Android phone.
Added #
PermissionDialogData.presentation: the presentation a prompt actually gets, so adialogBuildercan return a wholeDialogfor dialogs and just the content for sheets and snack bars.PermissionHistory.permanentlyDeniedPermissions: the native permissions a request reported permanently denied. Histories stored by 1.0.0 still load.FakePermissionPlatform.requestErrors: make only the requests that include a given permission throw.FakePermissionPlatformmodels two more iOS behaviors: location reportspermanentlyDeniedwhile Location Services are off, andlocationAlwaysreportspermanentlyDeniedonce while-in-use is granted, though a request still shows the upgrade prompt. Tests that script iOS location may see different results.PermissionPlan.settingsOnly, for a permission that only Settings can grant. A customresolvercan set it too.
1.0.0 #
First release.
-
Zero setup: dialogs find the app's root navigator automatically, so
init(),navigatorKeyandcontextare all optional. -
Short syntax:
AppPermission.camera.request(),.check(),.isAllowed, and for several at once[AppPermission.camera, AppPermission.microphone].request()/.areAllowed. -
BaabaPermissions:check,checkMany,request,requestMany,isAllowed,openAppSettings,openSettingsAndRecheck,watch,results,planFor,clearHistory. -
One request covers the whole flow: rationale → system prompt (stage by stage) → denied dialog with retry → permanently-denied dialog with numbered Settings steps → wait for the return from Settings and re-check → service-disabled dialog for location services and Bluetooth. Restricted permissions get an explanation.
-
AppPermission: 35 capabilities, each resolved to the right native permission for the OS and Android SDK level. For example, photos maps toREAD_MEDIA_IMAGESon 13+ and to storage below, Bluetooth maps to scan + connect on 12+, andlocationAlwaysrequests while-in-use first. A capability that doesn't exist on the platform isnotApplicableand counts as allowed. -
PermissionStateextendspermission_handler's status withserviceDisabled,notApplicableanderror.isAllowedis the one getter that is correct for every state. -
Remembers Android's permanent denial, which the OS never reports to a status check, in a pluggable
PermissionStore. -
Customization:
RationaleMode,DialogPolicy,maxRetries,PermissionStrings(with{app}/{permission}/{service}/{settings}placeholders, plusstringsBuilderfor i18n), per-permissionPermissionTexts,PermissionDialogTheme, dialog / bottom sheet / snack bar presentation per dialog kind,dialogBuilder,presenter, a resolver override, and per-callPermissionRequestOptions. -
Text direction:
PermissionConfig.textDirectionand per-callPermissionRequestOptions.textDirectionset RTL/LTR for every dialog, sheet, snack bar and gate, customdialogBuilderwidgets included. It defaults to the app's direction. AlsoPermissionDialogTheme.textAlign, andPermissionDialogData.textDirection/isRtlfor custom layouts. -
PermissionGate/PermissionGate.manywidget with a built-inPermissionRequiredViewfallback. -
PermissionObserverfor analytics and error reporting. -
No exceptions cross the API. Requests are serialized, since Android rejects overlapping ones. Dialogs fall back safely when
ScaffoldMessengeror Material localizations are missing. -
package:baaba_permission_handler/testing.dart:FakePermissionPlatform.