Chapter 32無料公開

アプリのライフサイクルを監視するProvider

村松龍之介
村松龍之介
2024.10.24に更新

起動しているアプリがバックグラウンド実行になった時、またはアクティブ状態に再開した等を検知して何か処理を実行したいことがあります。

そういったアプリのライフサイクルは、Flutterでは enum AppLifecycleState として定義されています。

enum AppLifecycleState {
  detached,
  resumed,
  inactive,
  hidden,
  paused,
}

https://api.flutter.dev/flutter/dart-ui/AppLifecycleState.html

これらのライフサイクル変更をRiverpodのProviderを使用して監視できるようにしましょう👍

全体の実装コードは以下のサンプルリポジトリの該当ファイルで確認できます↓
https://github.com/altive/flutter_app_template/blob/c9226861367edd335dd7b0ce59bc755f8fb646c2/packages/flutter_app/lib/util/providers/app_lifecycle_provider.dart

AppLifecycleStateを持つNotifierを作成

Notifierクラスで AppLifecycleState の変更を検知して状態を更新するために WidgetsBindingObserver ミックスインを使用しています。


class AppLifecycle extends _$AppLifecycle with WidgetsBindingObserver {
  
  AppLifecycleState build() {
    // プロバイダ構築時に監視を開始。
    final binding = WidgetsBinding.instance..addObserver(this);
    // プロバイダが破棄された時に監視を解除。
    ref.onDispose(() => binding.removeObserver(this));
    // 初期値として `resumed` を返している。
    return AppLifecycleState.resumed;
  }

  
  void didChangeAppLifecycleState(AppLifecycleState state) {
    // `AppLifecycleState` の変更を検知してNotifierが持つ状態を更新。
    this.state = state;
    super.didChangeAppLifecycleState(state);
  }
}

Notifierの build() メソッド内で、監視の開始を行い、 ref.onDispose を使ってプロバイダが破棄された時に監視を解除するようにします。

そして、 didChangeAppLifecycleState() メソッドをオーバーライドし、AppLifecycleState の変更を検知して state を更新しています。

super を使って継承元の didChangeAppLifecycleState メソッドも実行しておきましょう。

Widgetからの使用例

ref.listen を使用してアプリのライフサイクルを検知する例です。
ref が使用できる箇所であれば同じようにどこでも監視可能ですし、状態によってUIを更新したい場合は ref.watch を使用できます。

行いたい処理の責務にあった箇所でプロバイダを購読してライフサイクルを取得しましょう。

class App extends ConsumerWidget {
  const App({
    super.key,
  });

  
  Widget build(BuildContext context, WidgetRef ref) {
    // appLifecycleProviderの状態変更を監視
    ref.listen(
        appLifecycleProvider,
        // `next` に変更後の `AppLifecycleState` が入っています。
        // 任意の処理を行いましょう。
        (previous, next) => debugPrint('Previous: $previous, Next: $next'),
      );
    return const SplashPage();
  }
}

参考元

Riverpod作者であるRemiさんのこちらのポスト内コードを参考にさせていただきました。
https://x.com/remi_rousselet/status/1486675682491092997
https://x.com/riscait/status/1487008441386569732