Search code examples
flutter

Flutter AppLifecycleState.resumed does not detect app open (Android onStart)


According to the flutter AppLifecycleState documentation:

resumed → The application is visible and responding to user input

But It seems not to detect app opening.

I have implemented the AppLifecycleState in the main.dart:

SystemChannels.lifecycle.setMessageHandler((msg) {
    switch (msg) {
      case 'AppLifecycleState.paused':
        {
          function...
        }
        break;
      case 'AppLifecycleState.resumed':
        {
          function...
        }
        break;
    }
    return Future.value();
  });

How to check whether an application becomes in Foreground in Flutter (based on resume and app open)?


Solution

  • I don't know why the built in lifecycle management does not provide a app-open event, but this is how we do it.

    We define our own enum that replicates the AppLifecycleState where we add opened:

    /// see [AppLifecycleState] but added [opened]
    enum AppState {
      opened, // <--
      resumed,
      paused,
      inactive,
      detached,
    }
    

    Then we define a widget that uses initialState to trigger the opened event and use the WidgetsBindingObserver mixing to detect the other changes.

    
    class AppLifecycleTracker extends StatefulWidget {
      final Widget child;
      final void Function(AppState state) didChangeAppState;
    
      const AppLifecycleTracker({
        Key? key,
        required this.didChangeAppState,
        required this.child,
      }) : super(key: key);
    
      @override
      State<AppLifecycleTracker> createState() => _AppLifecycleTrackerState();
    }
    
    class _AppLifecycleTrackerState extends State<AppLifecycleTracker>
        with WidgetsBindingObserver {
      @override
      void initState() {
        super.initState();
        widget.didChangeAppState(AppState.opened);
        WidgetsBinding.instance.addObserver(this);
      }
    
      @override
      void dispose() {
        WidgetsBinding.instance.removeObserver(this);
        super.dispose();
      }
    
      @override
      void didChangeAppLifecycleState(AppLifecycleState state) {
        AppState s;
        switch (state) {
          case AppLifecycleState.resumed:
            s = AppState.resumed;
            break;
          case AppLifecycleState.inactive:
            s = AppState.inactive;
            break;
          case AppLifecycleState.paused:
            s = AppState.paused;
            break;
          case AppLifecycleState.detached:
            s = AppState.detached;
            break;
        }
        widget.didChangeAppState(state);
      }
    
      @override
      Widget build(BuildContext context) {
        return widget.child;
      }
    }
    

    Then be sure to add it at the very top of the widget tree to prevent it from being re-rendered (otherwise it would trigger the opened event again:

    runApp(
      AppLifecycleTracker(
        didChangeAppState: (state) => log(state.name),
        child: ...,
      )
    );