Deepak Ror
Deepak Ror

Reputation: 2254

How to handle app lifecycle with Flutter (on Android and iOS)?

Are there any Activity lifecycle methods in Flutter apps?

Like:

onCreate()
onResume()
onDestroy()

Or:

viewDidload()
viewWillAppear()

How to handle application lifecycle when making an app with Flutter?

Upvotes: 42

Views: 50382

Answers (4)

Praveena
Praveena

Reputation: 6980

Flutter version 3.13 added AppLifecycleListener so now you can listen to show, pause , resume , restart etc.

 _listener = AppLifecycleListener(
 onShow: () => _handleTransition('show'),
 onResume: () => _handleTransition('resume'),
 onHide: () => _handleTransition('hide'),
 onInactive: () => _handleTransition('inactive'),
 onPause: () => _handleTransition('pause'),
 onDetach: () => _handleTransition('detach'),
 onRestart: () => _handleTransition('restart'),
 // This fires for each state change. Callbacks above fire only for
 // specific state transitions.
 onStateChange: _handleStateChange,
);

For more info check this example from flutter repo

Upvotes: 12

user6490462
user6490462

Reputation:

There is a method called when the system put the app in the background or return the app to foreground named didChangeAppLifecycleState.

Example with widgets:

  class _AppLifecycleReactorState extends State<AppLifecycleReactor> 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) {
    setState(() { _notification = state; });
  }

  @override
  Widget build(BuildContext context) {
    return new Text('Last notification: $_notification');
  }
}

Also there are CONSTANTS to know the states that an application can be in, eg:

  1. inactive
  2. paused
  3. resumed
  4. suspending

The usage of these constants would be the value of the constant e.g:

const AppLifecycleState(state)

Upvotes: 59

CopsOnRoad
CopsOnRoad

Reputation: 268514

Run the following code, press the home button and then reopen the app to see it working. There are 4 AppLifecycleState:

resumed: The application is visible and responding to user input.

inactive: The application is in an inactive state and is not receiving user input.

paused: The application is not currently visible to the user, not responding to user input, and running in the background.

detached: The application is still hosted on a flutter engine but is detached from any host views.

Null safe code:

class MyPage extends StatefulWidget {
  @override
  _MyPageState createState() => _MyPageState();
}

class _MyPageState extends State<MyPage> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance!.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance!.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    print('Current state = $state');
  }

  @override
  Widget build(BuildContext context) => Scaffold();
}

Upvotes: 9

Sergio
Sergio

Reputation: 30765

To be notified when app goes to foreground or route popped you can inherit LifecycleState class and override onResume() and onPause() methods. LifecycleState class:

/// Inherit this State to be notified of lifecycle events, including popping and pushing routes.
///
/// Use `pushNamed()` or `push()` method to track lifecycle events when navigating to another route.
abstract class LifecycleState <T extends StatefulWidget> extends State<T>
    with WidgetsBindingObserver {
  ResumeResult resumeResult = new ResumeResult();
  bool _isPaused = false;

  AppLifecycleState lastAppState = AppLifecycleState.resumed;

  void onResume() {}

  void onPause() {}

  /// Use instead of Navigator.push(), it fires onResume() after route popped
Future<T> push<T extends Object>(BuildContext context, Route<T> route, [String source]) {
    _isPaused = true;
    onPause();

    return Navigator.of(context).push(route).then((value) {
        _isPaused = false;

        resumeResult.data = value;
        resumeResult.source = source;

        onResume();
        return value;
    });
}

/// Use instead of Navigator.pushNamed(), it fires onResume() after route popped
Future<T> pushNamed<T extends Object>(BuildContext context, String routeName, {Object arguments}) {
    _isPaused = true;
    onPause();

    return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments).then((value) {
        _isPaused = false;

        resumeResult.data = value;
        resumeResult.source = routeName;

        onResume();
        return value;
    });
}

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.paused) {
      if (!_isPaused) {
        onPause();
      }
    } else if (state == AppLifecycleState.resumed &&
        lastAppState == AppLifecycleState.paused) {
      if (!_isPaused) {
        onResume();
      }
    }
    lastAppState = state;
  }
}

class ResumeResult {
  dynamic data;
  String source;
}

Also make sure to start push new routes using push() or pushNamed() method.

Upvotes: 1

Related Questions