ScrollableTabRow
JetpackCompose
でタブとページャーを組み合わせたサンプルを探すとTabRow
を使ったものが多く見つかりますが、タブの数が多かったり可変だったりする場合はScrollableTabRow
を使った方が便利です。
基本的にはTabRow
をScrollableTabRow
に置き換えるだけで動作しますが、若干レイアウトを調整した方が良いと思います。
MainContent.kt
MainContent.kt
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun MainContent(
modifier: Modifier = Modifier,
state: MainContentState,
onAction: (action: MainContentAction) -> Unit
) {
val pagerState = rememberPagerState(initialPage = 0, pageCount = {state.itemCount})
Column(
modifier = modifier
) {
ScrollableTabRow(
selectedTabIndex = pagerState.currentPage,
edgePadding = 0.dp,
) {
for (i in 0 until state.itemCount) {
Box(
modifier = Modifier
.height(64.dp)
.clickable{
onAction(MainContentAction.ClickTab(i))
}
.padding(horizontal = 16.dp),
contentAlignment = Alignment.Center,
) {
Text(
text = "TabItem[${i}]",
style = MaterialTheme.typography.titleMedium,
)
}
}
}
HorizontalPager(
modifier = Modifier.fillMaxSize(),
state = pagerState
) { page ->
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = "PagerItem[${page}]",
style = MaterialTheme.typography.headlineLarge
)
}
}
}
LaunchedEffect(state.selectedIndex) {
if (state.selectedIndex != pagerState.currentPage) {
pagerState.animateScrollToPage(state.selectedIndex)
}
}
}
MainContentViewModel.kt
MainContentViewModel.kt
data class MainContentState(
val selectedIndex: Int = 0,
val itemCount: Int = 10
)
sealed interface MainContentAction {
data class ClickTab(val index: Int): MainContentAction
}
class MainContentViewModel: ViewModel() {
private val _state = MutableStateFlow(MainContentState())
val state = _state.asStateFlow()
fun onAction(action: MainContentAction) {
when (action) {
is MainContentAction.ClickTab -> clickTab(action.index)
}
}
private fun clickTab(index: Int) {
_state.update {
_state.value.copy(
selectedIndex = index
)
}
}
}
MainActivity.kt
MainActivity.kt
+ val viewModel by viewModels<MainContentViewModel>()
+ val state by viewModel.state.collectAsState()
+
+ MainContent(
+ modifier = Modifier.padding(innerPadding),
+ state = state,
+ onAction = viewModel::onAction
+ )