🐢

【KMP】LazyRowのクラッシュ問題を解決する

2025/02/22に公開

はじめに

Android StudioでKotlin Multiplatform(KMP)を使用して開発した際に、ScrollableTabRow内にLazyRowを配置するとアプリがクラッシュする問題が発生しました。

実行例
※実際の開発で使用しているものではありません
@Composable
fun ExampleTabRow(
    coroutineScope: CoroutineScope,
    pagerState: PagerState,
    tabItems: List<String>
) {
    Column {
        ScrollableTabRow(
            selectedTabIndex = pagerState.currentPage,
            edgePadding = 0.dp,
            indicator = { tabPositions ->
                Box(
                    Modifier
                        .tabIndicatorOffset(tabPositions[pagerState.currentPage])
                        .height(3.dp)
                        .background(color = Color(0xFF007993))
                )
            }
        ) {
            LazyRow(
                modifier = Modifier.fillMaxSize()
            ) {
                itemsIndexed(tabItems) { index, title ->
                    TabItem(
                        selected = pagerState.currentPage == index,
                        text = title,
                        onClick = { coroutineScope.launch { pagerState.scrollToPage(index) } },
                    )
                }
            }
        }
    }
}

この記事は、私がその問題を解決した方法について紹介するものになります。

結論

レイアウトの競合が発生しています。
ScrollableTabRow内にLazyRowではなく別のものを配置しましょう。

説明

ScrollableTabRowLazyRowはどちらもスクロール可能なコンポーネントです。
そのため、ScrollableTabRowの内部でLazyRowを使用するとレイアウトの競合が発生します。

これだけだと分かりにくいかもしれないので雑に例えてみます。
遊園地に「列に並んで乗るアトラクション」があるとした場合、「ジェットコースター🎢(ScrollableTabRow)」に「観覧車🎡(LazyRow)」を乗せると大混乱が起きますよね。

解決するには、ジェットコースターにそのまま並べば良いだけの話なのです。
今回、私はLazyRowを使わずにforEachIndexedで一つずつTabItemを追加することで、正常に動作するように修正しました。

修正後
※実際の開発で使用しているものではありません
@Composable
fun ExampleTabRow(
    coroutineScope: CoroutineScope,
    pagerState: PagerState,
    tabItems: List<String>
) {
    Column {
        ScrollableTabRow(
            selectedTabIndex = pagerState.currentPage,
            edgePadding = 0.dp,
            indicator = { tabPositions ->
                Box(
                    Modifier
                        .tabIndicatorOffset(tabPositions[pagerState.currentPage])
                        .height(3.dp)
                        .background(color = Color(0xFF007993))
                )
            }
        ) {
            tabItems.forEachIndexed { index, title ->
                TabItem(
                    selected = pagerState.currentPage == index,
                    text = title,
                    onClick = { coroutineScope.launch { pagerState.scrollToPage(index) } },
                )
            }
        }
    }
}

レイアウトを組む時は、各コンポーネントを適切に使用しましょう!

さいごに

ここまで記事を読んでくださり、ありがとうございました!

UIを実装する時は各コンポーネントのスクロール特性を理解することが大事ですね。

皆さんも素敵なハッピーKMPライフを!!!🌸

GitHubで編集を提案
CocBan Tech Blog

Discussion