flutter-permission-handler
flutter-permission-handler copied to clipboard
Notification callback when permission has changed
🚀 Feature Requests
Notification callback when permission has changed
Contextualize the feature
User modify permission (by example location) in the device settings. The app is notified that the permission has changed
Describe the feature
Platforms affected (mark all that apply)
- [x] :iphone: iOS
- [x] :robot: Android
We have to research if this is possible on Android / iOS. If someone knows about callbacks on the native Android / iOS side we could use please let me know.
Just an idea - add status checks when the app is resumed, eg when it comes back from being backgrounded.
The developer adds something like:
StreamSubscription<PermissionStatus> _sub;
initState() {
final stream = PermissionHandler().getChangesStream(PermissionGroup.notification);
_sub = stream.listen((PermissionStatus status) {
// make use of the new `status` *when* it changes
});
}
dispose() {
_sub.cancel();
}
Then this plugin knows that it needs to check certain statuses, when the app comes back from the background and publish to the stream.
I did it with pure Flutter, but it depends on Flutter's WidgetBindingObserver, while the plugin can use its native channel to add hooks to the app lifecycle.
class RegisterScreen extends StatefulWidget {
@override
State createState() => _RegisterScreenState();
}
class _RegisterScreenState extends State<RegisterScreen>
with WidgetsBindingObserver {
StreamSubscription<PermissionStatus> _sub;
@override
void initState() {
super.initState();
// Add a lifecycle observer
WidgetsBinding.instance.addObserver(this);
final stream = getChangesStream(PermissionGroup.notification);
_sub = stream.listen((PermissionStatus status) {
print("status: $status");
});
}
// The following should be part of the plugin itself
Map<PermissionGroup, StreamController<PermissionStatus>> _controllers = {};
Map<PermissionGroup, PermissionStatus> _statuses = {};
// The following should be part of the plugin itself
Stream<PermissionStatus> getChangesStream(PermissionGroup group) {
StreamController<PermissionStatus> controller = _controllers[group];
if (controller == null) {
controller = StreamController.broadcast(onListen: () {
if (_statuses[group] == null) {
// Do an initial status check
PermissionHandler().checkPermissionStatus(group).then((status) {
_statuses[group] = status;
});
}
});
_controllers[group] = controller;
}
return controller.stream;
}
@override
void dispose() {
_sub.cancel();
// Remove the lifecycle observer
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
// The following should be part of the plugin itself, and not rely on the observer, but probably some native channel
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
for (final group in _controllers.keys) {
final controller = _controllers[group];
if (controller.hasListener) {
PermissionHandler().checkPermissionStatus(group).then((status) {
if (!controller.isPaused && !controller.isClosed && controller.hasListener && status != _statuses[group]) {
controller.add(status);
}
_statuses[group] = status;
});
}
}
}
}
}
this is a good idea, I had to implement similar code with WidgetBindingObserver
Its not a perfect solution since it only checks on lifecycle changes and doesnt help when the app is running in the background, but it is better than nothing.
Just sharing this in case it's useful I accomplished this by creating a two builder widgets
- Observes app lifecycle changes in a more contained way
- Uses the lifecycle observer widget in combination with the permissions to get callbacks on change
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
class AppLifecycleObserving extends StatefulWidget {
final Widget Function(
BuildContext context,
Stream<AppLifecycleState> stateStream,
) builder;
AppLifecycleObserving({
Key key,
@required this.builder,
}) : super(key: key);
@override
_AppLifecycleObservingState createState() => _AppLifecycleObservingState();
}
class _AppLifecycleObservingState extends State<AppLifecycleObserving>
with WidgetsBindingObserver {
final _subject = BehaviorSubject<AppLifecycleState>();
@override
void didChangeDependencies() {
super.didChangeDependencies();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
_subject.add(state);
}
@override
Widget build(BuildContext context) {
return widget.builder(context, _subject.stream);
}
}
And
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wink/screens/achievements/widgets/lifecycle_observing.dart';
typedef _PermisisonChangeBuilder = Widget Function(
BuildContext context,
PermissionStatus status,
);
/// Combines permission checking with background notifiers to maintain the
/// latest state of permissions whether the app is foregrounded or not
class PermisisonChangeBuilder extends StatelessWidget {
final Permission permission;
final _PermisisonChangeBuilder builder;
const PermisisonChangeBuilder({
Key key,
@required this.permission,
@required this.builder,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return AppLifecycleObserving(
builder: (context, stateStream) => StreamBuilder<AppLifecycleState>(
stream: stateStream,
builder: (context, snapshot) => FutureBuilder<PermissionStatus>(
future: Permission.contacts.status,
builder: (context, snapshot) => builder(context, snapshot.data),
),
),
);
}
}
Then can be used via
return PermisisonChangeBuilder(
permission: Permission.contacts,
builder: (context, status) {
if (status == null) {
return const LoadingPage();
}
return _renderPermissionStatus(status);
},
);
The solutions help for the cases when user change a permission through app settings only. For the case the case of a user allowing a permission from within the app will not fire the stream above. There are cases where a plugin like camera asks for permissions by itself and this would not fire the stream mentioned above. I don't know if a native solution is possible. If someone with that knowledge can help out, it would be great
@aytunch , @KieranLafferty , @gkrawiec @avioli @mvanbeusekom @fvisticot Hi, Guys, I am able to request remote notification, but wondering how we can set action for notification tap ?
there are no native platform APIs for listening but current workarounds just flatmap an applifecycle state observable and asyncmap that into a new event being the (changed or not) permission status - which is pretty straightforward with flutter:services API and does not need any native implementation
Similar issue: #784.