Flutter 2.2.0から2.2.3にアップグレードをしたところ、エラーなしでできました。
今日は、FlutterのWidgetsBindingObserverを使って、ライフサイクルステートで、どのコードが実行されているのかログを使って確認していきたいと思います。
WidgetsBindingObserverの設定
まず、下記のコマンドで新規のFlutterアプリを作成します。
flutter create lifecycle_observer
lib/main.dart
にて、下記のクラスを定義します。
class _StateHandler extends WidgetsBindingObserver {
}
上記のクラスのdidChangeAppLifecycleState(AppLifecycleState state)
メソッドをオーバーライドして、下記のように定義します。
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
print("Resumed and Foreground Active in iOS, resumed in Android...");
break;
case AppLifecycleState.paused:
print("Paused and Background in iOS, created in Android...");
break;
case AppLifecycleState.inactive:
print("Inactive and Foreground Inactive in iOS, started in Android...");
break;
case AppLifecycleState.detached:
print(
"Detached and Unattached or Suspended in iOS, initialized or destroyed in Android...");
break;
default:
print("Unknown State");
}
}
このコードはアプリのライフサイクルステートに変更があった場合に実行されます。Flutterアプリのライフサイクルステートは4つ定義されており、それぞれのステートに変化した際に、print
関数を使って、ログに出力します。
次にmain()
関数に以下のようにコードを追加して、上記で定義したクラスをオブザーバーとして追加します。
void main() {
runApp(MyApp());
WidgetsBinding.instance!.addObserver(new _StateHandler());
}
iOSでの挙動を確認
それでは、早速起動してみます。iOSシミュレータを起動した状態で下記のコマンドを実行します。
flutter run
通常のログの中に、下記の文字列が出力されていることがわかります。
flutter: Resumed and Foreground Active in iOS, resumed in Android...
この文字列は、ライフサイクルステートがresumed
のとき、つまりアプリが操作可能な状態になった時点で出力されています。
iPhone 11 Proのシミュレータを利用しているので、画面下部にマウスをあてて、少しドラッグアップすると、今度は下記の文字列が出力されます。
flutter: Inactive and Foreground Inactive in iOS, started in Android...
アプリは画面上に見えているが操作できない状態、つまりライフサイクルステートがinactive
の状態に変化したというです。
再び、操作できる状態に戻すと、resumed
にステートが変わったことを示すメッセージが出力されます。
電源ボタンなどを押して、アプリを画面から消すと下記の文字列が表示され、ライフサイクルステートがpaused
に移行したことがわかります。
flutter: Paused and Background in iOS, created in Android...
そこから、もう一度アプリを表示させると、今度は以下のように文字列が出力されます。
flutter: Inactive and Foreground Inactive in iOS, started in Android...
flutter: Resumed and Foreground Active in iOS, resumed in Android...
これは、ライフサイクルステートが、paused
からinactive
、そしてresumed
へと順番に変化していることを示しています。
detached
ステートで表示されるはずの文字列は確認できませんでした。これは当然と言えば当然ですが、detached
のステートにあるということはすでにアプリは動作していない状態だと考えられるので、文字列の出力自体がされないため、見ることができないということですね。
Androidでの挙動を確認
今度はAndroidのエミュレータで同様の操作をしてみます。Androidエミュレータを起動した状態で、flutter run
を実行します。しかし、起動はするのですが、Resumed and Foreground Active in iOS, resumed in Android...
という文字列は表示されません。
エミュレータの「Overview」ボタンを押して、画面上に見えているが操作できないinactive
の状態にしてみます。すると下記の文字列が出力されます。
I/flutter ( 4654): Inactive and Foreground Inactive in iOS, started in Android...
I/flutter ( 4654): Paused and Background in iOS, created in Android...
この時点で、バックグラウンドの状態にまで進んでいるのがわかります。iOSの時と挙動がかなり異なることがわかります。
操作できる状態にアプリを戻すと、下記の文字列が表示されます。
I/flutter ( 4654): Resumed and Foreground Active in iOS, resumed in Android...
ここは想定内ですね。ここで電源ボタンを押してアプリを画面から消してみると、下記の文字列が再び表示されます。
I/flutter ( 4654): Inactive and Foreground Inactive in iOS, started in Android...
I/flutter ( 4654): Paused and Background in iOS, created in Android...
これも想定通りです。
ここから電源を入れて、再び操作できる状態に戻すと下記の文字列のみが出力されました。
I/flutter ( 4654): Resumed and Foreground Active in iOS, resumed in Android...
これは意外です。inactive
状態を経由せずにresumed
状態になっています。
detached
のメッセージはAndroidでも表示させることはできませんでした。
まとめ
これまでにも、AndroidとiOSのアプリには起動時の挙動が異なるということを経験したことがあったのですが、今回の検証でその理由が分かったような気がします。意外だったのは、Androidで起動した際のステート遷移の特徴でした。Kotlinでアクティビティのステート遷移を類似の方法で確認した時は、わかりやすいと感じたのですが、FlutterアプリのAndroid上でのライフサイクルには挙動にクセがあるように感じます。
参考サイト:
https://api.flutter.dev/flutter/widgets/WidgetsBindingObserver-class.html