Jetpack Compose Navigation × トランジションで発生する Lifecycle イベントずれ問題への対処法
はじめに
Jetpack Compose で画面遷移を実装する際、NavHost
や AnimatedNavHost
を利用することで、美しいアニメーション付きの画面切り替えが可能です。
しかしこのとき、画面がまだ表示中(トランジション中)に Lifecycle.Event.ON_STOP
が呼ばれてしまう という問題に遭遇することがあります。
本記事では、この問題の発生原因と、それに対処するための実例コードを紹介します。
ON_STOP
が早く呼ばれるのか?
なぜ Jetpack Compose Navigation では、新たな画面へ遷移する際に現在の画面の NavBackStackEntry
がバックスタックへ退避され、そのライフサイクルがバックグラウンド状態へと遷移します。
これに伴い、ON_PAUSE
や ON_STOP
といったイベントが即座に発生します。
一方、アニメーションによるトランジション中は、ユーザーから見ると「まだ前の画面が残っている」状態です。しかし、ライフサイクル的にはすでにバックグラウンドに回っているため、ON_STOP
が表示上の非表示より先に呼び出されてしまうのです。
発生する問題
もし ON_STOP
イベント内でクリーンアップ処理や特定の UI 状態リセットを行っている場合、ユーザーから見てまだ画面が存在する最中に処理が実行され、視覚的な不整合が生じる恐れがあります。
DisposableEffect
で表示上の終了時に処理を実行
対処策:DisposableEffect
は、Composable が Composition
から外れるタイミングで onDispose
ブロックを実行します。つまり、画面が実際に UI ツリーから取り除かれたとき(=視覚的にも画面が消えたとき)に処理を行うことが可能です。
以下は実際のコード例です。
DisposableEffect(lifecycleOwner) {
onDispose {
// ここで実際に画面が消えた後の処理を書く
}
}
このようにすることで、ライフサイクルイベントとは独立したタイミング(実際の画面除去時)で処理を実行できます。
その他のアプローチ
-
アニメーション完了を自前で検知
AnimatedContent
やTransition
API を用いて独自にアニメーション状態を管理し、アニメーション終了時に処理を呼び出すことも可能です。 -
ViewModel や SharedFlow の活用
画面終了イベントを ViewModel 側で管理し、アニメーション完了後にイベントを発行するなど、アプリ独自の状態管理を組み合わせることも考えられます。
まとめ
Jetpack Compose Navigation とアニメーションを組み合わせると、表示上とライフサイクル上の状態が必ずしも同期しないため、ON_STOP
イベントが予想より早く呼ばれてしまう問題が発生します。
この問題に対しては、DisposableEffect
の onDispose
を利用することで、実際に画面が UI ツリーから取り除かれるタイミングで処理を実行でき、表示上の不整合を避けることができます。
もし同様の課題に直面している場合は、ぜひこの方法を試してみてください。
Discussion