While clicking on the tab with HorizontalPager in android Jetpack Compse, it jumps to the last tab I add the code to see the problem visible
This is my code ->
data class TabPage(val title: String?, val icon: ImageVector? = null, var screen: (@Composable () -> Unit)? = null)
@OptIn(ExperimentalPagerApi::class)
@Composable
fun SwipeableTabLayout(
modifier: Modifier = Modifier,
tabPages: List<TabPage>,
scrollable: Boolean = false,
isIconTab: Boolean = false,
unselectedContentColor: Color = MaterialTheme.colors.textColorTertiary,
tabBackgroundColor: Color = MaterialTheme.colors.surface,
tabContentColor: Color = MaterialTheme.colors.primary,
backgroundColor: Color = MaterialTheme.colors.surface
) {
val pagerState = rememberPagerState()
val scope = rememberCoroutineScope()
Surface(color = backgroundColor) {
Column {
if (scrollable) {
ScrollableTab(
modifier = modifier,
tabPages = tabPages,
selectedTabIndex = pagerState.currentPage,
onSelectedTab = { scope.launch { pagerState.animateScrollToPage(tabPages.indexOf(it)) } },
isIconTab = isIconTab,
unselectedContentColor = unselectedContentColor,
backgroundColor = tabBackgroundColor,
contentColor = tabContentColor
)
} else {
FixedTab(
modifier = modifier,
tabPages = tabPages,
selectedTabIndex = pagerState.currentPage,
onSelectedTab = { scope.launch { pagerState.animateScrollToPage(tabPages.indexOf(it)) } },
isIconTab = isIconTab,
unselectedContentColor = unselectedContentColor,
backgroundColor = tabBackgroundColor,
contentColor = tabContentColor
)
}
HorizontalPager(
state = pagerState,
count = tabPages.size,
content = { page -> tabPages[page].screen?.invoke() })
}
}
}
@Composable
fun ScrollableTab(
modifier: Modifier = Modifier,
tabPages: List<TabPage>,
selectedTabIndex: Int,
isIconTab: Boolean = false,
unselectedContentColor: Color,
backgroundColor: Color,
contentColor: Color,
onSelectedTab: (TabPage) -> Unit
) {
ScrollableTabRow(
modifier = modifier,
edgePadding = 0.dp,
selectedTabIndex = selectedTabIndex,
backgroundColor = backgroundColor,
contentColor = contentColor
) {
tabPages.forEachIndexed { index, tabPage ->
if (isIconTab) {
LeadingIconTab(
selected = index == selectedTabIndex,
unselectedContentColor = unselectedContentColor,
onClick = { onSelectedTab(tabPage) },
text = { Text(text = tabPage.title ?: "") },
icon = tabPage.icon?.let { icon -> { Icon(imageVector = icon, contentDescription = null) } } ?: {}
)
} else {
Tab(
selected = index == selectedTabIndex,
unselectedContentColor = unselectedContentColor,
onClick = { onSelectedTab(tabPage) },
text = { Text(text = tabPage.title ?: "") },
icon = tabPage.icon?.let { icon -> { Icon(imageVector = icon, contentDescription = null) } }
)
}
}
}
}
@Composable
fun FixedTab(
modifier: Modifier = Modifier,
tabPages: List<TabPage>,
selectedTabIndex: Int,
isIconTab: Boolean = false,
unselectedContentColor: Color = MaterialTheme.colors.textColorTertiary,
backgroundColor: Color,
contentColor: Color,
onSelectedTab: (TabPage) -> Unit
) {
TabRow(selectedTabIndex = selectedTabIndex, backgroundColor = backgroundColor, contentColor = contentColor) {
tabPages.forEachIndexed { index, tabPage ->
if (isIconTab) {
LeadingIconTab(
modifier = modifier,
selected = index == selectedTabIndex,
unselectedContentColor = unselectedContentColor,
onClick = { onSelectedTab(tabPage) },
text = { Text(text = tabPage.title ?: "") },
icon = tabPage.icon?.let { icon -> { Icon(imageVector = icon, contentDescription = null) } } ?: {}
)
} else {
Tab(
modifier = modifier,
selected = index == selectedTabIndex,
unselectedContentColor = unselectedContentColor,
onClick = { onSelectedTab(tabPage) },
text = tabPage.title?.let { title -> { Text(text = title) } },
icon = tabPage.icon?.let { icon -> { Icon(imageVector = icon, contentDescription = null) } }
)
}
}
}
}
And this is where it is used ->
val tabPages = listOf(
TabPage(title = SnackbarType.ERROR.value, screen = { LoadScreen(SnackbarType.ERROR) }),
TabPage(title = SnackbarType.WARNING.value, screen = { LoadScreen(SnackbarType.WARNING) }),
TabPage(title = SnackbarType.SUCCESS.value, screen = { LoadScreen(SnackbarType.SUCCESS) }),
TabPage(title = SnackbarType.INFO.value, screen = { LoadScreen(SnackbarType.INFO) })
)
Scaffold(
backgroundColor = MaterialTheme.colors.background,
topBar = {
CatalogTopAppBar(
title = stringResource(id = R.string.snackbar),
subtitle = stringResource(id = R.string.core_components),
navigateUp = { onNavigate(Destination.Up) },
)
},
content = {
SwipeableTabLayout(tabPages = tabPages, scrollable = true)
}
)
@Composable
private fun LoadScreen(snackbarType: SnackbarType) {
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Text(text = "One line", style = MaterialTheme.typography.payback.headline6, color = MaterialTheme.colors.onSurface)
LoadSnackbar(message = stringResource(R.string.snackbar_1_line_message_text), snackbarType = snackbarType)
Text(text = "Two lines", style = MaterialTheme.typography.payback.headline6, color = MaterialTheme.colors.onSurface)
LoadSnackbar(message = stringResource(R.string.snackbar_2_lines_message_text), maxLine = 2, snackbarType = snackbarType)
Text(text = "One line with action", style = MaterialTheme.typography.payback.headline6, color = MaterialTheme.colors.onSurface)
LoadSnackbar(
message = stringResource(R.string.snackbar_1_line_message_text),
snackbarType = snackbarType,
actionLabel = "Action"
)
Text(text = "Two lines with action", style = MaterialTheme.typography.payback.headline6, color = MaterialTheme.colors.onSurface)
LoadSnackbar(
message = stringResource(R.string.snackbar_2_lines_message_text),
snackbarType = snackbarType,
actionLabel = "Action",
maxLine = 2
)
val oneLineMessage = stringResource(R.string.snackbar_1_line_message_text)
ContainedButton(
modifier = Modifier
.fillMaxWidth()
.padding(top = dimensionResource(R.dimen.ds_spacing_06)),
text = "Show 1 line Snackbar",
onClick = {
scope.launch {
scaffoldState.snackbarHostState.showSnackbar(message = oneLineMessage, duration = SnackbarDuration.Short)
}
},
)
val twoLineMessage = stringResource(R.string.snackbar_2_lines_message_text)
ContainedButton(
modifier = Modifier.fillMaxWidth(),
text = "Show 2 lines Snackbar",
onClick = {
scope.launch {
scaffoldState.snackbarHostState.showSnackbar(message = twoLineMessage, duration = SnackbarDuration.Short)
}
},
)
}
Box(modifier = Modifier.fillMaxSize()) {
SnackbarSetting(
snackbarHostState = scaffoldState.snackbarHostState,
snackbarType = snackbarType,
maxLine = 2,
modifier = Modifier.align(Alignment.BottomCenter)
)
}
}
If the LoadScreen() function just loads a simple text, it works well but when you add more items, while clicking it jumps to the last tab
e.g. with this LoadScreen() works fine ->
@Composable
private fun LoadScreen(snackbarType: SnackbarType) {
val scaffoldState = rememberScaffoldState()
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
Text(text = "One line", style = MaterialTheme.typography.payback.headline6, color = MaterialTheme.colors.onSurface)
LoadSnackbar(message = stringResource(R.string.snackbar_1_line_message_text), snackbarType = snackbarType)
}
Box(modifier = Modifier.fillMaxSize()) {
SnackbarSetting(
snackbarHostState = scaffoldState.snackbarHostState,
snackbarType = snackbarType,
maxLine = 2,
modifier = Modifier.align(Alignment.BottomCenter)
)
CodePudding user response: