How do I check if the Flutter application is in the foreground or not?
Asked Answered
T

10

120

I don't want to show notification when the app is in foreground. How can I check live state of my app?

Tootle answered 14/8, 2018 at 6:32 Comment(0)
C
183

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.
Chas answered 19/10, 2018 at 10:55 Comment(9)
Do I need to add WidgetsBindingObserver in all screen widget classes or only in a widget which is attached to the main class?Groundmass
You need to add with WidgetsBindingObserver to the widgets in which you want to do some reaction on AppLyfecycle eventsChas
The example is working as expected but I also get a message when the app is resumed: E/BpSurfaceComposerClient(10905): Failed to transact (-1). What may be the cause ?Theodoratheodore
You can always use enums with this as well (recommended) AppLifecycleState.resumed for exampleWarhorse
Can we asume that the initial state is resumed?Roma
Valentine - if app has some permissions, user granted them, app is in background and user revoke granted permission then tries to use the app from background to foreground then app shows a white blank screen. This is the issue. Can we kill the background app if user tries to run app after revoking granted permission. Please suggest. Thanks.Glare
This doesn't work when the app is killed, for example in iOSEjectment
It's not working on Desktop just fyiWarhorse
Plz remove the question mark in 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
F
97

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;
    }
}
Flophouse answered 28/5, 2020 at 20:32 Comment(6)
you forgot to close curly braces at the end. Please update the answer. Thanks.Glare
Don't copy paste this answer on its own - it complements the other answer, as it states -- this won't work unless you also have : @override initState() { super.initState(); WidgetsBinding.instance.addObserver(this); ... } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); }Fransis
StackOverflow shows accepted answers just below the question. The newer answers come before older answers. Always point to the answer of someone by adding @somebody answer. People will be miss guided by your statement "above answer" as now above is a newer answer which is approx 6 months newer than your answer, and I believe you meant by above answer is "Valentina Konyukhova" 's answer.Jotter
How do you implement this at app level? Where would this code go?Inshore
There is a missing state (AppLifecycleState.hidden) in your switch, see this error message: The type 'AppLifecycleState' is not exhaustively matched by the switch cases since it doesn't match 'AppLifecycleState.hidden'. Documentation Try adding a default case or cases that match 'AppLifecycleState.hidden'.Zerline
btu if app goes in background using back button why it is not detecting its status after that. with home button it does provide sataus but with backbvutton it doesn not. can someone please tell me what is the reason behind that?Annabel
P
38

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();
}
Plethora answered 10/11, 2020 at 10:13 Comment(0)
A
32

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.

Ankeny answered 26/4, 2021 at 20:13 Comment(2)
This answer should have way more upvotes. The damn inactive/paused BS causes so many unexpected Bugs. This should be always as easy as this plugin provides it. The fact that you need a plugin for a simple problem like this is the reason alien civilizations still avoid earth.Jestude
How can I use this when I have multiple screen? The plugin doesn't get the state on the other screen, and even with the example app, I can't navigate to the next screen, I get this error Another exception was thrown: Navigator operation requested with a context that does not include a Navigator.Bhili
A
13
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;
    }
  }

}
Aceves answered 14/6, 2022 at 6:37 Comment(0)
C
10

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;
          }
      }
    
    }
Clamatorial answered 4/1, 2022 at 5:6 Comment(0)
G
6

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
}
Gratitude answered 20/9, 2022 at 16:32 Comment(0)
D
5

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
}
Dermatology answered 10/6, 2021 at 7:22 Comment(2)
You must add 'WidgetsBinding.instance?.addObserver(this);' on the initState() for this code to work. For flutter < 2.0 remove the '?'Mimesis
As @Mimesis describes - to use this approach additional implementation is required, example: stackoverflow.com/a/63641646Submediant
E
2

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.

Earthward answered 1/11, 2021 at 15:42 Comment(0)
R
2

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.

Github

Rehearsal answered 18/6, 2023 at 18:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.