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
}))
);
}
...
}
Related
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?
I have been battling with this flutter bloc problem. I am currently using flutter Bloc 7.0.1. The BlocConsumer doesn't listen to the state changes at all. Anytime I enter values inside the search field, event is been called and state is yielded but the listener fail to listen to state changes.
This issue is really driving me mad.
STATE
part of 'people_bloc.dart';
#immutable
abstract class PeopleState {}
class PeopleInitial extends PeopleState {}
class PeopleLoadingState extends PeopleState {
#override
List<Object?> get props => [];
}
class SearchLoadingState extends PeopleState {
#override
List<Object?> get props => [];
}
BLOC
List<SearchPeopleResponseData> people = [];
#override
Stream<PeopleState> mapEventToState(
PeopleEvent event,
) async* {
if (event is SearchPeopleEvent) {
yield SearchLoadingState();
try {
var token = await getToken();
//print(token);
SearchPeopleResponse responseData =
await client.getPeople(token!, event.term);
if (responseData.status == 200) {
yield GetSearchResultState(getPeopleResponse: responseData);
} else {
yield PeopleErrorState(message: responseData.msg);
print("loadingE");
}
} catch (e) {
//print("error msg here ${e.toString()}");
PeopleErrorState(message: e.toString());
}
}
EVENT
part of 'people_bloc.dart';
#immutable
abstract class PeopleEvent {
const PeopleEvent();
}
class GetPeopleEvent extends PeopleEvent {
final String term;
GetPeopleEvent({required this.term});
#override
List<Object> get props => [term];
}
class SearchPeopleEvent extends PeopleEvent {
final String term;
SearchPeopleEvent({required this.term});
#override
List<Object> get props => [term];
}
VIEW
Widget build(BuildContext context) {
return BlocConsumer<PeopleBloc, PeopleState>(
listener: (context, state) {
print("Listener has been called");
if (state is GetSearchResultState) {
loading = false;
print("Result Found in view");
} else if (state is SearchLoadingState) {
loading = true;
print("Search loading");
} else if (state is PeopleLoadingState) {
loading = true;
}
See screenshot
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
Currently Im using native_admob_flutter for my flutter app. I also included AppOpenAds and Interstital ad within the app.
I want to display the AppOpen ad when user open the app/ resume to the app, here is how I do it in main.dart:
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
switch (state) {
case AppLifecycleState.resumed:
loadAndDisplayAppOpenAd();
break;
case AppLifecycleState.inactive:
break;
case AppLifecycleState.paused:
break;
case AppLifecycleState.detached:
break;
}
}
However I found that displaying Interstial ad will also change the app state -> inactive -> paused -> resume, which trigger AppOpen ad.
This really create bad user experinence. Is there a way to overcome this? Thank you!
You may need a variable to make sure that app has been paused by user action, and not by interstitial.
static bool pausedByInterstitial = false;
...
class AppLifecycleReactor extends WidgetsBindingObserver {
final AppOpenAdManager appOpenAdManager;
AppLifecycleReactor({required this.appOpenAdManager});
static bool pausedByInterstitial = false;
#override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
if (state == AppLifecycleState.resumed && !pausedByInterstitial) {
appOpenAdManager.showAdIfAvailable();
}
}
}
...
showInter() {
AppLifecycleReactor.pausedByInterstitial = true;
InterstitialListener(
...
onAdHiddenCallback: (ad) {
AppLifecycleReactor.pausedByInterstitial = false;
},
...
);
}
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