I'm using the local_auth package to lock app services after login if the user closes the app
what I want is to call the authentication function in the resume state of AppLifecycleState (when the user opens the app again)
but this prompt authentication infinitely
class _AppState extends State<App> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) async {
switch (state) {
case AppLifecycleState.inactive:
break;
case AppLifecycleState.paused:
break;
case AppLifecycleState.resumed:
{
await _auth.authenticate(
useErrorDialogs: true,
stickyAuth: true,
);
}
break;
case AppLifecycleState.detached:
break;
}
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
This will call authentication forever
how to solve this?
Related
I have a StatefulWidget and at the beginning of the State method's build I do
WidgetsBinding.instance
.addObserver(LifecycleEventHandler(resumeCallBack: () async {
if (mounted) {
refreshState();
}
}));
This is the code of the LifecycleEventHandler
class LifecycleEventHandler extends WidgetsBindingObserver {
final AsyncCallback resumeCallBack;
LifecycleEventHandler({
this.resumeCallBack,
});
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
switch (state) {
case AppLifecycleState.resumed:
if (resumeCallBack != null) {
await resumeCallBack();
}
break;
case AppLifecycleState.inactive:
break;
case AppLifecycleState.paused:
break;
case AppLifecycleState.detached:
break;
}
}
}
the resumeCallBack is being called every time the app is resumed, even if the page is not visible to the user (if I navigate to another page without removing this from the stack).
Is there a way to run this callback only if the page is visible to the user? I don't want to dispose the state/page but just avoid unnecessary state updates
I am using this plugin to update my location in firebase. When the app is in foreground everything works perfectly but as soon as my app goes in background then the location update service stops,I tried using didChangeAppLifecycleState but I can't seem to get it to work,
This is my implementaion so far..
class HomeTabPage extends StatefulWidget {
#override
_HomeTabPageState createState() => _HomeTabPageState();
}
class _HomeTabPageState extends State < HomeTabPage >
with AutomaticKeepAliveClientMixin, WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) async {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.inactive ||
state == AppLifecycleState.detached) return;
final isBackground = state == AppLifecycleState.paused;
if (isBackground) {
print("### paused");
StreamSubscription < Position > backgroundStreamSubscription =
Geolocator.getPositionStream().listen((Position position) {
initPositionPoint = position;
Geofire.setLocation(
currentFirebaseUser.uid, position.latitude, position.longitude);
});
}
}
From Github issues #53 and #444 of the flutter-geolocator repo it seems that it doesn't support background location tracking. It seems that some folks have been using background_locator as an alternative, so you might want to look at that.
hai I need to call some function when app will reopen from sleep
after user can put app in minimize then when the reopen the app that function have to call in flutter
You need to listen to lifecycle events like this
class LifecycleEventHandler extends WidgetsBindingObserver {
final AsyncCallback resumeCallBack;
final AsyncCallback suspendingCallBack;
LifecycleEventHandler({
this.resumeCallBack,
this.suspendingCallBack,
});
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
switch (state) {
case AppLifecycleState.resumed:
if (resumeCallBack != null) {
await resumeCallBack();
}
break;
case AppLifecycleState.inactive:
case AppLifecycleState.paused:
case AppLifecycleState.detached:
if (suspendingCallBack != null) {
await suspendingCallBack();
}
break;
}
}
}
class AppWidgetState extends State<AppWidget> {
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(
LifecycleEventHandler(resumeCallBack: () async => setState(() {
// here the app has resumed
}))
);
}
...
}
I implemented the WidgetsBindingObserver, but the app is NEVER sent to the background so it doesn't recognize the AppLifecycleState.resumed
this is the current implementation
#override
void didChangeAppLifecycleState(AppLifecycleState state) async {
print('\n\ndidChangeAppLifecycleState');
switch (state) {
case AppLifecycleState.resumed:
print('\n\nresumed');
_mymethod();
break;
case AppLifecycleState.inactive:
print('\n\ninactive');
break;
case AppLifecycleState.paused:
print('\n\npaused');
break;
case AppLifecycleState.detached:
print('\n\ndetached');
break;
}
}
to simulate the process i do the next in android
run the project as --release
open the widget with the WidgetsBindingObserver
open another app (like chrome or phone settings)
return to the app
when returning to the app i can see my widget on screen, the app doesn't restart, but NONE of the prints appears on the console not event the print('\n\ndidChangeAppLifecycleState'); and _mymethod(); is never executed
The WidgetsBindingObserver mixin requires a bit more work than merely implementing the interface. You also need to add the following to your widget state class:
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
I have a Flutter app that contains sensitive medial information and business wants this information to be hidden when the app is put into the background, including the Recent Apps screen.
I have added WidgetsBindingObserver and am listening to the events correctly. Resumed state correctly fires and sends user back to login page; however, the paused event doesn't do anything on receipt of the state. For reference I have tried pushing a new screen onto the stack as well as popping all screens until reaching the login but neither works.
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
switch (state) {
case AppLifecycleState.paused:
Navigator.of(context).push(new PageRouteBuilder(
pageBuilder: (_, __, ___) => new Splash(
inBackground: true,
),
));
break;
case AppLifecycleState.resumed:
Navigator.of(context).pushNamed('/login');
break;
default:
break;
}
}
I expect that when the paused event is received to be able to change the screen to protect this sensitive info. Any ideas are welcome!
EDIT: Most recent code.
import 'package:boxview_mobile_flutter/screens/splash/index.dart';
import 'package:boxview_mobile_flutter/services/shared_prefs.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
class PushNotifications extends StatefulWidget {
#override
_PushNotificationsState createState() => _PushNotificationsState();
}
class _PushNotificationsState extends State<PushNotifications> with WidgetsBindingObserver {
bool loggedOut = false;
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
#override
Widget build(BuildContext context) {
return Container(child: Splash(loggedOut: this.loggedOut));
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.paused:
print("Paused");
break;
case AppLifecycleState.inactive:
print("inactive");
setState(() {
loggedOut = true;
});
break;
case AppLifecycleState.suspending:
print("suspending");
break;
case AppLifecycleState.resumed:
setState(() {
loggedOut = false;
});
print("resumed");
break;
}
}
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_firebaseMessaging.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
},
);
_firebaseMessaging.requestNotificationPermissions(const IosNotificationSettings(sound: true, badge: true, alert: true));
_firebaseMessaging.onIosSettingsRegistered.listen((IosNotificationSettings settings) {
print("Settings registered: $settings");
});
_firebaseMessaging.getToken().then((String token) {
assert(token != null);
SharedPreferencesHelper.setFirebaseToken(token);
});
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
Splash is just a splash page and the boolean loggedOut param just says don't forward to login page.
dont forget to add with WidgetsBindingObserver to your state class
class YourClass extends StatefulWidget { ....
class _YourClassState extends State<BottomNavigator> with WidgetsBindingObserver { ...
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
AppLifecycleState _notification;
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.paused:
print("Paused");
setState(() {
_notification = state;
});
break;
case AppLifecycleState.inactive:
setState(() {
_notification = state;
});
print("inactive");
break;
case AppLifecycleState.suspending:
setState(() {
_notification = state;
});
print("suspending");
break;
case AppLifecycleState.resumed:
setState(() {
_notification = state;
});
print("resumed");
break;
}
}
and change your app state with _notification
for example
#override
Widget build(BuildContext context) {
return _notification == AppLifecycleState.inactive
? Scaffold(
body: Text("inactive"),
)
: YourRealWidget()
you can look more infromation for WidgetsBindingObserver from here