I don't want to show notification when the app is in foreground. How can I check live state of my app?
In your State<...> class you need to implement WidgetsBindingObserver interface and listen for widget state changes. Something like this:
class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
AppLifecycleState? _notification;
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_notification = state;
});
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
...
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
}
Then when you want to know the state, check the _notification value (e.g. _notification == AppLifecycleState.resumed
) or its index property.
_notification == null => no state changes happened,
0 - resumed,
1 - inactive,
2 - paused.
WidgetsBinding.instance?.removeObserver(this)
etc! For one thing, the instance can't be null, so it throws a warning, and for another thing, if it were to be null at some point, I doubt you could add or remove an observer to it! 😏 So it's quite misleading, really. –
Myxomatosis To extend on @CopsOnRoad's answer, you can use a switch
statement to make it nice and neat:
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
print("app in resumed");
break;
case AppLifecycleState.inactive:
print("app in inactive");
break;
case AppLifecycleState.paused:
print("app in paused");
break;
case AppLifecycleState.detached:
print("app in detached");
break;
}
}
@override initState() { super.initState(); WidgetsBinding.instance.addObserver(this); ... } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); }
–
Fransis Simply create a bool
variable which will keep track of all your background/foreground stuff.
Full code:
class _HomePageState extends State<HomePage> with WidgetsBindingObserver {
// This variable will tell you whether the application is in foreground or not.
bool _isInForeground = true;
@override
void initState() {
super.initState();
WidgetsBinding.instance!.addObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
_isInForeground = state == AppLifecycleState.resumed;
}
@override
void dispose() {
WidgetsBinding.instance!.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) => Scaffold();
}
There is a package named flutter_fgbg for this.
Example:
FGBGNotifier(
onEvent: (event) {
print(event); // FGBGType.foreground or FGBGType.background
},
child: ...,
)
Or:
subscription = FGBGEvents.stream.listen((event) {
print(event); // FGBGType.foreground or FGBGType.background
});
// in dispose
subscription.cancel();
Why:
Flutter has WidgetsBindingObserver to get notified when app changes its state from active to inactive states and back. But it actually includes the state changes of the embedding Activity/ViewController as well. So if you have a plugin that opens a new activity/view controller(eg: image picker) or in iOS if you start a FaceID prompt then WidgetsBindingObserver will report as the app is inactive/resumed.
This plugin on the other hand reports the events only at app level. Since most apps need only background/foreground events this plugin is implemented with just those events. In iOS, plugin reports didEnterBackgroundNotification and willEnterForegroundNotification notifications and in Android, plugin reports these using androidx.lifecycle:lifecycle-process package.
Checkout example/ project to see the differences in action.
Example link.
class YourClassState extends State<YourClass> with WidgetsBindingObserver{
@override
void initState(){
WidgetsBinding.instance.addObserver(this);
super.initState();
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
print("app in resumed");
break;
case AppLifecycleState.inactive:
print("app in inactive");
break;
case AppLifecycleState.paused:
print("app in paused");
break;
case AppLifecycleState.detached:
print("app in detached");
break;
}
}
}
People have already posted the answer but this answer is for developers using Getx architecture. You will be able to use the same approach but instead of using it on our stateless widget use it in the controller page. This method helps you to manage foreground and background activities when using Getx state management architecture
class QuotesController extends GetxController with WidgetsBindingObserver{
@override
void onInit() async{
super.onInit();
WidgetsBinding.instance?.addObserver(this);
}
@override
void onClose() {
WidgetsBinding.instance?.removeObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) async{
switch(state){
case AppLifecycleState.resumed:
await player.play();
break;
case AppLifecycleState.inactive:
await player.stop();
break;
case AppLifecycleState.paused:
await player.stop();
break;
case AppLifecycleState.detached:
await player.stop();
// TODO: Handle this case.
break;
}
}
}
I was looking for an easy way to implement that solution and here is the one that works for me:
1. Create a LifecycleEventHandler Class
In this class add two callbacks as attributes, the suspendingCallBack
will be invoked when the app goes to the background, and the resumeCallBack
will be invoked when the app returns to the foreground.
class LifecycleEventHandler extends WidgetsBindingObserver {
final AsyncCallback resumeCallBack;
final AsyncCallback suspendingCallBack;
LifecycleEventHandler({
required this.resumeCallBack,
required this.suspendingCallBack,
});
@override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
print('state >>>>>>>>>>>>>>>>>>>>>> : ${state}');
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;
}
}
}
2. Listen to the State change.
In the main page of our app we can create a variable to save the lifecycle state, just when the app goes into the background
we set the value to true
, otherwise the value will be false
.
class MainPageState extends State<MainPage> {
bool isAppInactive = false; // if the app is Inactive do some thing
@override
initState(){
super.initState();
WidgetsBinding.instance.addObserver(
LifecycleEventHandler(
resumeCallBack: () async {
// The app is now resumed, so let's change the value to false
setState(() {isAppInactive = false; });
}, suspendingCallBack: () async {
// The app is now inactive, so let's change the value to true
setState(() {isAppInactive = true; });
})
);
}
}
And then you can use that variable value to do what you want
if(isAppInactive){
// then do some thing
}
You can use a global variable to save the state for easy to use.
For example:
1. Create global variable:
AppLifecycleState appLifecycleState = AppLifecycleState.detached;
2. AddObserver in your AppState
class:
@override
void initState() {
super.initState();
WidgetsBinding.instance?.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance?.removeObserver(this);
super.dispose();
}
3. Save state:
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
appLifecycleState = state;
}
4. Now you can use it easily everywhere when you need:
if (appLifecycleState == AppLifecycleState.paused) {
// in background
}
If you need it on app start, e.g. to differentiate between normal app launch and push notification, you can read directly from this:
WidgetsBinding.instance?.lifecycleState
It will be detached for push (that is when a push message is received in a callback) and resumed for normal app launch.
If you just need to know from your flutter_local_notifications method if the app is in the background, widgets, and states are not required at all.
This simple class will do the job:
import 'package:flutter/material.dart';
final lifecycleEventHandler = LifecycleEventHandler._();
class LifecycleEventHandler extends WidgetsBindingObserver {
var inBackground = true;
LifecycleEventHandler._();
init() {
WidgetsBinding.instance.addObserver(lifecycleEventHandler);
}
@override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
switch (state) {
case AppLifecycleState.resumed:
inBackground = false;
print('in foreground');
break;
case AppLifecycleState.inactive:
case AppLifecycleState.paused:
case AppLifecycleState.detached:
inBackground = true;
print('in background');
break;
}
}
}
In your main.dart:
void main() {
WidgetsFlutterBinding.ensureInitialized();
lifecycleEventHandler.init();
runApp(const MainApp());
}
Now, it works and properly prints in background/in foreground.
Then from your notifications method just check inBackground
variable.
Future _showNotificationWithDefaultSound(notifPlugin) async {
if (!lifecycleEventHandler.inBackground){
return;
}
//otherwise show a notification
...
}
Credits: link
If you want to update your widgets based on lifecycle events using Riverpod, check this answer.
© 2022 - 2024 — McMap. All rights reserved.