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 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.