😽

Jetpack Compose Navigation × トランジションで発生する Lifecycle イベントずれ問題への対処法

2024/12/18に公開

はじめに

Jetpack Compose で画面遷移を実装する際、NavHostAnimatedNavHost を利用することで、美しいアニメーション付きの画面切り替えが可能です。
しかしこのとき、画面がまだ表示中(トランジション中)に Lifecycle.Event.ON_STOP が呼ばれてしまう という問題に遭遇することがあります。

本記事では、この問題の発生原因と、それに対処するための実例コードを紹介します。

なぜ ON_STOP が早く呼ばれるのか?

Jetpack Compose Navigation では、新たな画面へ遷移する際に現在の画面の NavBackStackEntry がバックスタックへ退避され、そのライフサイクルがバックグラウンド状態へと遷移します。
これに伴い、ON_PAUSEON_STOP といったイベントが即座に発生します。

一方、アニメーションによるトランジション中は、ユーザーから見ると「まだ前の画面が残っている」状態です。しかし、ライフサイクル的にはすでにバックグラウンドに回っているため、ON_STOP が表示上の非表示より先に呼び出されてしまうのです。

発生する問題

もし ON_STOP イベント内でクリーンアップ処理や特定の UI 状態リセットを行っている場合、ユーザーから見てまだ画面が存在する最中に処理が実行され、視覚的な不整合が生じる恐れがあります。

対処策:DisposableEffect で表示上の終了時に処理を実行

DisposableEffect は、Composable が Composition から外れるタイミングで onDispose ブロックを実行します。つまり、画面が実際に UI ツリーから取り除かれたとき(=視覚的にも画面が消えたとき)に処理を行うことが可能です。

以下は実際のコード例です。

DisposableEffect(lifecycleOwner) {
    onDispose {
        // ここで実際に画面が消えた後の処理を書く
    }
}

このようにすることで、ライフサイクルイベントとは独立したタイミング(実際の画面除去時)で処理を実行できます。

その他のアプローチ

  • アニメーション完了を自前で検知
    AnimatedContentTransition API を用いて独自にアニメーション状態を管理し、アニメーション終了時に処理を呼び出すことも可能です。

  • ViewModel や SharedFlow の活用
    画面終了イベントを ViewModel 側で管理し、アニメーション完了後にイベントを発行するなど、アプリ独自の状態管理を組み合わせることも考えられます。

まとめ

Jetpack Compose Navigation とアニメーションを組み合わせると、表示上とライフサイクル上の状態が必ずしも同期しないため、ON_STOP イベントが予想より早く呼ばれてしまう問題が発生します。
この問題に対しては、DisposableEffectonDispose を利用することで、実際に画面が UI ツリーから取り除かれるタイミングで処理を実行でき、表示上の不整合を避けることができます。

もし同様の課題に直面している場合は、ぜひこの方法を試してみてください。

Discussion