0
Help us understand the problem. What are the problem?

posted at

updated at

Jetpack ComposeのNavigationを使うと遷移先の画面が繰り返し表示される問題

こんにちは。
最近は宣言的UIが流行っていますね。
Flutterでよく書いていてもう見慣れたのですが、初めて見たときはネストの量にギョッとした記憶があります。
最近はJetpack Composeに挑戦中です。
そんな中でログイン機能を実装していてどハマりしたので共有したいなと思い今回の記事を書くに至りました。

まず状況として、「ログインしていない場合は上からログイン画面を被せる」という仕様を満たそうとした時に、ログイン画面が繰り返し表示されてしまう不具合にハマりました。

これが起きてしまった原因は、結論から言いますと、「Composable内で画面遷移をしようとしたから」です。
具体的にどんなコードを書いていたかというと、

HomeScreen.kt
@Composable
fun HomeScreen(
    viewModel: HomeViewModel,
    navController: NavController
) {
    val uiState by produceState(
        initialValue = HomeUiState(isLoading = true)
    ) {
        val currentUser = viewModel.currentUser()
        value = HomeUiState(isLoading = false, currentUser = currentUser)
    }

    if (!uiState.isLoading && uiState.currentUser == null) {
        navController.navigate(ScreenRoutes.login)
    }

こんな感じで、currentUserがnullのとき(ログインしていないとき)はログイン画面に遷移するというコードを書いていました。
Composable内でnavigate()をしているのがまずかったようです。

全然原因がわからずブレークポイントを貼ったり公式ドキュメントをあちこち読んで途方に暮れていました。
が、よく見たら・・・

You should only call navigate() as part of a callback and not as part of your composable itself, to avoid calling navigate() on every recomposition.

めちゃくちゃ書いてた・・・
Composableの一部としてnavigate()を呼ぶな、と公式ドキュメントにめちゃくちゃ書いてました・・・

とても辛い気持ちになりましたが、これが原因でした。

ちなみに解決策としては

    if (!uiState.isLoading && uiState.currentUser == null) {
        LaunchedEffect(Unit) {
            navController.navigate(ScreenRoutes.login)
        }
    }

これでオッケーでした。

Jetpack Compose自体がまだまだ出来立てだから不具合が多いのかなと思っていましたが、想像以上に自分の知識不足だったパターンが多いです。
ちゃんと勉強しなきゃですね・・・

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
0
Help us understand the problem. What are the problem?