В версии Flutter 3.13 в Framework была добавлена возможность использования AppLifecycleListener
, чтобы отслеживать изменения жизненного цикла приложения и реагировать на запросы выхода из приложения. Какие особенности у этого нового подхода и как он отличается от старого?
Проще говоря, до версии Flutter 3.13 мы обычно использовали метод didChangeAppLifecycleState
класса WidgetsBindingObserver
для отслеживания состояния жизненного цикла приложения. Однако этот метод был довольно «грубым», так как просто возвращал состояние AppLifecycleState
, а пользователю приходилось самостоятельно обрабатывать эти данные. При этом требовалось использовать mixin
, чтобы внедрить WidgetsBindingObserver
.
Новый AppLifecycleListener
представляет собой улучшенную версию метода didChangeAppLifecycleState
. Он позволяет создать более полную цепочку событий жизненного цикла приложения, что делает его использование удобнее и API более понятными.
Сначала стоит отметить, что AppLifecycleListener
является полноценным классом, поэтому вам не потребуется использовать mixin
. Вам достаточно будет создать объект AppLifecycleListener
там, где это требуется:
final AppLifecycleListener _listener;
AppLifecycleState? _state;
``````markdown
@override
void initState() {
super.initState();
_state = SchedulerBinding.instance.lifecycleState;
_listener = AppLifecycleListener(
onShow: () => _handle_transition('show'),
onResume: () => _handle_transition('resume'),
onHide: () => _handle_transition('hide'),
onInactive: () => _handle_transition('inactive'),
onPause: () => _handle_transition('pause'),
onDetach: () => _handle_transition('detach'),
onRestart: () => _handle_transition('restart'),
// Этот коллбэк вызывается при каждом изменении состояния. Коллбэки выше вызываются только при конкретных переходах состояний.
on_state_change: _handle_state_change,
);
}
void _handle_transition(String name) {
print("########################## main $name");
}
Кроме того, AppLifecycleListener
уже правильно организует все коллбэки в зависимости от состояния AppLifecycleState
, что делает их использование более прямолинейным.
И наконец, с помощью AppLifecycleListener
можно легче отслеживать и записывать изменения всего жизненного цикла приложения, поскольку он уже предоставляет вам необходимые методы обратного вызова. - Переход от состояния inactive
к состоянию resumed
происходит при вызове метода onResume
.
detached
к состоянию resumed
происходит при вызове метода onStart
.С помощью обратного вызова AppLifecycleListener
мы можем более удобно и наглядно воспринимать цепочку изменения всего жизненного цикла, а в версии 3.13 также был введен новый статус — «hidden
», хотя этот статус фактически не используется на платформах Android/iOS.
Понятие `hidden` не имеет практического значения для мобильных приложений, так как оно определено здесь только для унификации всех состояний.
Хотя состояние `hidden` не существует на мобильных платформах, вы всё равно можете получать обратные вызовы этого состояния с использованием `AppLifecycleListener` на платформе Android. Причины этого будут объяснены позже.
Давайте рассмотрим несколько состояний `AppLifecycleState`:
### detached
Приложение может иметь запущенный движок Flutter, но сам виджет отсутствует, например, нет `FlutterView`. Это состояние по умолчанию до инициализации Flutter.
Это значит, что движок может работать даже если нет активного вида. Обычно это состояние присутствует только на платформах iOS и Android, хотя оно является начальным по умолчанию для всех платформ. В общих случаях его можно использовать для слежения за выходом приложения.
### resumed
Приложение находится в состоянии выполнения, которое доступно для взаимодействия и видимо.
Например, на платформах iOS и macOS это соответствует состоянию активности в переднем плане.
На платформе Android это обычно соответствует состоянию `onResume`, но связано с методом `Activity.onWindowFocusChanged`.
Когда есть несколько активностей:
- Только активность с фокусом равным true переходит в состояние `onResume` и становится `resumed`.
- Остальные активности с фокусом равным false переходят в состояние `onPause` и становятся `inactive`.Определение состояния зависит от того, установлен ли фокус в методе `Activity.onWindowFocusChanged`. Однако по умолчанию Flutter использует одну активность, поэтому это соответствует состоянию `onResume`.

### inactive
По крайней мере один вид приложения видимый, но ни один из них не имеет фокуса. На небольших небраузерных платформах это соответствует приложению, которое не находится в переднем плане, но всё ещё имеет видимое окно.
- В случае с Web, это соответствует окну или вкладке, которая не имеет фокуса.
- На платформах iOS и macOS это соответствует состоянию Flutter-вьюхи, которая находится в переднем плане, но неактивна (например, когда появляется звонок, биометрическая аутентификация, переход между приложениями или открыто меню управления).
- На платформе Android это соответствует состоянию, когда метод `Activity.onPause()` уже был вызван или состояние `onResume()`, когда фокус не получен (разделённый экран, скрытый за другими приложениями, режим picture-in-picture).
Пожалуйста, обратите внимание, что в данном контексте терминология связана с жизненным циклом активностей в мобильной разработке и может отличаться в зависимости от конкретной реализации. На Android и iOS, состояние inactive может рассматриваться как предшествующее переходу в состояния hidden и paused.#### приостановлено
Приложение недоступно для пользователя и не реагирует на его действия.
Когда приложение находится в этом состоянии, Engine не вызывает методы `PlatformDispatcher.onBeginFrame` и `PlatformDispatcher.onDrawFrame`.
> Это состояние достигается только на iOS и Android.

#### скрыто
Все представления приложения скрыты.
- На iOS и Android указывает на то, что приложение скоро перейдет в состояние приостановлено.
- На ПК указывает на минимизацию или отсутствие видимости на рабочем столе.
- Веб — указывает на окна или вкладки, которые не видны.
Из этого можно сделать вывод, что жизненный цикл приложений действительно отличается на разных платформах, а `AppLifecycleState` служит для маскировки этих различий. Однако из-за исторических причин названия состояний в Flutter не всегда совпадают с названиями состояний на конкретной платформе, например:
> На Android, когда система вызывает метод `Activity.onPause`, Flutter переходит в состояние inactive; но когда Android вызывает метод `Activity.onStop`, Flutter переходит в состояние paused.

> Разумеется, если приложение было завершено через менеджер задач, аварийное завершение работы или сигнал kill, пользователь не получит никаких уведомлений.Таким образом, если вы вернетесь к рассмотрению состояния `hidden`, вы поймёте, почему это состояние не имеет практического значения на Android и iOS, так как оно предназначено для ПК (минимизация/невидимость). Однако, если вы используете слушатель `AppLifecycleListener`, вы заметите, что всё же можете получить уведомление о состоянии `hidden`, например на Android и iOS:- Из активного режима в фоновый: inactive -> hide -> pause
- Из фонового режима обратно в активный: restart -> show -> resume
Хотя в native Android и iOS нет состояния `hidden`, почему же оно всё равно запускается в Dart?
Это связано с тем, что Flutter в своём Framework обеспечивает согласованность жизненного цикла на уровне Dart, "добирая" пропущенные состояния.
Например, при выходе в фоновое состояние, native отправляет только два состояния — inactive и pause. При получении состояния pause, в методе `_generateStateTransitions` добавляется состояние hidden "вручную", чтобы вызвать метод `onHide`. Поэтому, когда мы используем `AppLifecycleState` в Android и iOS, обычно лучше не полагаться на callback `onHide`, так как он не предназначен для жизненного цикла мобильных устройств.
Кроме того, `AppLifecycleState` предоставляет метод `onExitRequested`, но этот метод не поддерживает сценарий перехвата кнопки "Назад" аналогично Android. Вместо этого запрос на выход можно запустить через `ServicesBinding.instance.exitApplication(AppExitType exitType)`. Метод `onExitRequested` будет перехватывать такой запрос при условии, что был передан тип выхода `AppExitType.cancelable`.
> То есть вызов типа `ServicesBinding.instance.exitApplication(AppExitType.cancelable);` активирует метод `onExitRequested`. Кроме того, ответ на `System.exitApplication` реализован только на ПК, а на мобильных устройствах не поддерживается.
```dart
@override
void initState() {
super.initState();
_listener = AppLifecycleListener(
onExitRequested: _handleExitRequest,
);
}
``````dart
Future<AppExitResponse> _handleExitRequest() async {
var result = await showDialog(
context: context,
builder: (context) => AlertDialog.adaptive(
title: const Text('Выход'),
content: const Text('Вы уверены?'),
actions: [
TextButton(
child: const Text('Нет'),
onPressed: () {
Navigator.of(context).pop(false);
},
),
TextButton(
child: const Text('Да'),
onPressed: () {
Navigator.of(context).pop(true);
},
),
],
));
final AppExitResponse response =
result ? AppExitResponse.exit : AppExitResponse.cancel;
return response;
}
Итог:
AppLifecycleListener
заключается в том, что его не нужно смешивать с другими компонентами, и он позволяет отслеживать цепочку жизненного цикла через коллбэки.AppLifecycleState
могут не совпадать с аналогичными настройками в нативных системах.hidden
в Android и iOS не существует; это просто промежуточное состояние, созданное для унификации.onExitRequested
работает только на персональных компьютерах.Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )