1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/CarGuo-GSYFlutterBook

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
Flutter-N31.md 16 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 10.03.2025 00:06 5767d61

Небольшие хитрости Flutter 3.13 — новый жизненный цикл приложения AppLifecycleListener

В версии 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`.

![image](http://img.cdn.guoshuyu.cn/20230821_FL/image5.png)

### inactive

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

Пожалуйста, обратите внимание, что в данном контексте терминология связана с жизненным циклом активностей в мобильной разработке и может отличаться в зависимости от конкретной реализации. На Android и iOS, состояние inactive может рассматриваться как предшествующее переходу в состояния hidden и paused.#### приостановлено

Приложение недоступно для пользователя и не реагирует на его действия.

Когда приложение находится в этом состоянии, Engine не вызывает методы `PlatformDispatcher.onBeginFrame` и `PlatformDispatcher.onDrawFrame`.

> Это состояние достигается только на iOS и Android.

![](http://img.cdn.guoshuyu.cn/20230821_FL/image6.png)

#### скрыто

Все представления приложения скрыты.

- На iOS и Android указывает на то, что приложение скоро перейдет в состояние приостановлено.
- На ПК указывает на минимизацию или отсутствие видимости на рабочем столе.
- Веб — указывает на окна или вкладки, которые не видны.

Из этого можно сделать вывод, что жизненный цикл приложений действительно отличается на разных платформах, а `AppLifecycleState` служит для маскировки этих различий. Однако из-за исторических причин названия состояний в Flutter не всегда совпадают с названиями состояний на конкретной платформе, например:

> На Android, когда система вызывает метод `Activity.onPause`, Flutter переходит в состояние inactive; но когда Android вызывает метод `Activity.onStop`, Flutter переходит в состояние paused.

![](http://img.cdn.guoshuyu.cn/20230821_FL/image7.png)

> Разумеется, если приложение было завершено через менеджер задач, аварийное завершение работы или сигнал 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 могут не совпадать с аналогичными настройками в нативных системах.
  • В Flutter состояние может различаться в зависимости от того, есть ли один или несколько активных экранов.
  • Состояние hidden в Android и iOS не существует; это просто промежуточное состояние, созданное для унификации.
  • Метод onExitRequested работает только на персональных компьютерах.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/CarGuo-GSYFlutterBook.git
git@api.gitlife.ru:oschina-mirror/CarGuo-GSYFlutterBook.git
oschina-mirror
CarGuo-GSYFlutterBook
CarGuo-GSYFlutterBook
master