Flutter - Using AppLifecycleListener Examples

This tutorial shows you how to use AppLifecycleListener in Flutter.

A running Flutter application has a state at a particular time. The state can change at any time. For example, when the user switches to another application. The state lifecycle can be seen on the diagram below, with the blue arrows only happening on iOS and Android.

Flutter - Application Lifecycle

Flutter 3.13 introduced a new class called AppLifecycleListener. It provides an easy way to add listeners when the application state changes. Before, you have to create a state class that uses WidgetsBindingObserver and override the didChangeAppLifecycleState method. With AppLifecycleListener, you don't need to do that. Just create an AppLifecycleListener instance and add some callback functions to be called when the state changes.

Using AppLifecycleListener

To use AppLifecycleListener, you just need to call the constructor which can be seen below.

  AppLifecycleListener AppLifecycleListener({ 
    WidgetsBinding? binding, 
    void Function()? onResume, 
    void Function()? onInactive, 
    void Function()? onHide, 
    void Function()? onShow, 
    void Function()? onPause, 
    void Function()? onRestart, 
    void Function()? onDetach, 
    Future Function()? onExitRequested, 
    void Function(AppLifecycleState)? onStateChange,
  })

You can create a state variable to store the instance and instantiate it in the initState method. You also need to dispose it inside the dispose method, which will be called when the widget is removed from the tree.

  class _MyPageState extends State<MyPage> {
  
    late final AppLifecycleListener _listener;
  
    @override
    void initState() {
      super.initState();
      _listener = AppLifecycleListener(

      );
    }
  
    @override
    void dispose() {
      _listener.dispose();
      super.dispose();
    }
  }

There is no required argument. Most of the arguments are for passing a callback function to be called when the application state changes to a specific state. There are onResume, onInactive, onHide, onShow, onPause, onRestart, and onDetach. For those arguments, you have to pass a void callback function with no parameter. When the state changes, Flutter will only call the related callback. For example if the state changes to paused, the function passed as onPause will be invoked.

  class _MyPageState extends State<MyPage> {
  
    late final AppLifecycleListener _listener;
    late AppLifecycleState? _currentState;
    final List<String> _states = <String>[];
  
    @override
    void initState() {
      super.initState();
      _currentState = SchedulerBinding.instance.lifecycleState;
  
      if (_currentState != null) {
        _states.add(_currentState!.name);
      }
  
      _listener = AppLifecycleListener(
        onShow: () => _pushState('show'),
        onResume: () => _pushState('resume'),
        onHide: () => _pushState('hide'),
        onInactive: () => _pushState('inactive'),
        onPause: () => _pushState('pause'),
        onDetach: () => _pushState('detach'),
        onRestart: () => _pushState('restart'),
        // other arguments
      );
    }
  
    void _pushState(String state) {
      setState(() {
        _states.add(state);
      });
    }
  }

To listen every time the state changes, you can pass a function as the onStateChange. The passed function must have a parameter AppLifecycleState which represents the latest state.

  class _MyPageState extends State<MyPage> {
  
    late final AppLifecycleListener _listener;
    late AppLifecycleState? _currentState;
    final List<String> _states = <String>[];
  
    @override
    void initState() {
      super.initState();
      _currentState = SchedulerBinding.instance.lifecycleState;
  
      if (_currentState != null) {
        _states.add(_currentState!.name);
      }
  
      _listener = AppLifecycleListener(
        onStateChange: _handleStateChange,
        // other arguments
      );
    }
  
    void _handleStateChange(AppLifecycleState state) {
      setState(() {
        _currentState = state;
      });
    }
  }

There is also an argument named onExitRequested which can be used to ask the application if it allows exiting the application for cases where the exit is cancelable.

  class _MyPageState extends State<MyPage> {
  
    late final AppLifecycleListener _listener;
    late AppLifecycleState? _currentState;
    final List<String> _states = <String>[];
  
    @override
    void initState() {
      super.initState();
      _currentState = SchedulerBinding.instance.lifecycleState;
  
      if (_currentState != null) {
        _states.add(_currentState!.name);
      }
  
      _listener = AppLifecycleListener(
        onExitRequested: _handleExitRequest,
        // other arguments
      );
    }
  
    Future<AppExitResponse> _handleExitRequest() async {
      /// Exit can proceed.
      return AppExitResponse.exit;

      /// Cancel the exit.
      return AppExitResponse.cancel;
    }
  }

For the binding argument, you can optionally pass a WidgetsBinding to listen to for application lifecycle events. The value defaults to WidgetsBinding.instance. You can substitute it for testing or other specialized bindings.

Full Code

  import 'dart:ui';
  
  import 'package:flutter/material.dart';
  import 'package:flutter/scheduler.dart';
  
  void main() => runApp(const MyApp());
  
  class MyApp extends StatelessWidget {
  
    const MyApp({super.key});
  
    @override
    Widget build(BuildContext context) {
      return const MaterialApp(
        title: 'Woolha.com Flutter Tutorial',
        home: MyPage(),
        debugShowCheckedModeBanner: false,
      );
    }
  }
  
  class MyPage extends StatefulWidget {
  
    const MyPage({super.key});
  
    @override
    State<StatefulWidget> createState() {
      return _MyPageState();
    }
  }
  
  class _MyPageState extends State<MyPage> {
  
    late final AppLifecycleListener _listener;
    late AppLifecycleState? _currentState;
    final List<String> _states = <String>[];
  
    @override
    void initState() {
      super.initState();
      _currentState = SchedulerBinding.instance.lifecycleState;
  
      if (_currentState != null) {
        _states.add(_currentState!.name);
      }
  
      _listener = AppLifecycleListener(
        onShow: () => _pushState('show'),
        onResume: () => _pushState('resume'),
        onHide: () => _pushState('hide'),
        onInactive: () => _pushState('inactive'),
        onPause: () => _pushState('pause'),
        onDetach: () => _pushState('detach'),
        onRestart: () => _pushState('restart'),
        onStateChange: _handleStateChange,
        onExitRequested: _handleExitRequest,
      );
    }
  
    @override
    void dispose() {
      _listener.dispose();
      super.dispose();
    }
  
    void _pushState(String state) {
      setState(() {
        _states.add(state);
      });
    }
  
    void _handleStateChange(AppLifecycleState state) {
      setState(() {
        _currentState = state;
      });
    }
  
    Future<AppExitResponse> _handleExitRequest() async {
      /// Exit can proceed.
      return AppExitResponse.exit;
  
      /// Cancel the exit.
      return AppExitResponse.cancel;
    }
  
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: const Text('Woolha.com Flutter Tutorial'),
          backgroundColor: Colors.teal,
        ),
        body: SizedBox(
          width: double.infinity,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('Current State:\n${_currentState.toString()}', textAlign: TextAlign.center),
              const SizedBox(height: 20),
              Text('States:\n${_states.join('\n')}', textAlign: TextAlign.center),
              const SizedBox(height: 20),
            ],
          ),
        ),
      );
    }
  }

AppLifecycleListener Parameters

  • WidgetsBinding? binding: The WidgetsBinding to listen to for application lifecycle events.
  • void Function()? onResume: A callback to be called when a view in the application gains input focus.
  • void Function()? onInactive: A callback to be called when the application loses input focus.
  • void Function()? onHide: A callback to be called when the application is hidden.
  • void Function()? onShow: A callback to be called when the application is shown.
  • void Function()? onPause: A callback to be called when the application is paused.
  • void Function()? onRestart: A callback to be called when the application is resumed after being paused.
  • void Function()? onDetach: A callback to be called when an application has exited, and detached all host views from the engine.
  • Future Function()? onExitRequested: A callback used to ask the application if it allows exiting the application for cases where the exit is cancelable.
  • void Function(AppLifecycleState)? onStateChange: A callback to be called every time the state changes.

Summary

The AppLifecycleListener can be used to listen for application state changes. You can pass callback functions to be invoked when the state changes to a specific one or pass a callback function that will be invoked every time the state changes. Inside the passed functions, you can write your own logic to handle the state changes.