accompanist
accompanist copied to clipboard
Nested HorizontalPager add top / bottom padding for no reason
Hello I'm trying to use 2 HorizontalPagers - One inside another. The first one display items correctly but if I duplicate the same logic to the nested one - it starts to add an additional padding on some screens for no reasons
Really need this one asap Appreciate you a lot
`import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.* import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.google.accompanist.pager.ExperimentalPagerApi import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.pagerTabIndicatorOffset import com.google.accompanist.pager.rememberPagerState import kotlinx.coroutines.launch
@OptIn(ExperimentalPagerApi::class) @Composable fun TestCard() { val mainPagerState = rememberPagerState()
val mainTitles = listOf(
"First",
"Second",
"Third",
"Forth",
"Fifth"
)
val tabIndex = mainPagerState.currentPage
val coroutineScope = rememberCoroutineScope()
Column(Modifier.fillMaxSize()) {
ScrollableTabRow(
selectedTabIndex = tabIndex,
edgePadding = 16.dp,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
modifier = Modifier.pagerTabIndicatorOffset(
mainPagerState,
tabPositions
),
color = MaterialTheme.colors.primary
)
},
backgroundColor = Color.Transparent
) {
mainTitles.forEachIndexed { index, tabTitle ->
Tab(
modifier = Modifier
.height(
height = 30.dp
),
selected = tabIndex == index,
onClick = {
coroutineScope.launch {
mainPagerState.animateScrollToPage(index)
}
},
text = {
Text(
modifier = Modifier
.alpha(
alpha = if (tabIndex == index) 1f else 0.85f
),
text = tabTitle,
style = MaterialTheme.typography.h6.copy(
fontSize = 14.sp,
letterSpacing = -(0.25).sp,
color = if (tabIndex == index) Color.Black else Color.Magenta
)
)
}
)
}
}
HorizontalPager(
modifier = Modifier.fillMaxSize(),
count = mainTitles.size,
state = mainPagerState
) { index ->
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
when (mainTitles[index]) {
"First" -> {
for (a in 0..2) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(
16.dp
)
.background(Color.Green),
text = mainTitles[index],
style = MaterialTheme.typography.h6
)
}
InnerHorizontalPager()
}
"Second" -> {
for (a in 0..3) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(
16.dp
)
.background(Color.Green),
text = mainTitles[index],
style = MaterialTheme.typography.h6
)
}
}
"Third" -> {
for (a in 0..7) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(
16.dp
)
.background(Color.Green),
text = mainTitles[index],
style = MaterialTheme.typography.h6
)
}
}
"Forth" -> {
for (a in 0..15) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(
16.dp
)
.background(Color.Green),
text = mainTitles[index],
style = MaterialTheme.typography.h6
)
}
}
"Fifth" -> {
for (a in 0..3) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(
16.dp
)
.background(Color.Green),
text = mainTitles[index],
style = MaterialTheme.typography.h6
)
}
}
}
}
}
}
}
@OptIn(ExperimentalPagerApi::class) @Composable fun InnerHorizontalPager() { val additionalTitles = listOf( "First", "Second", "Third", "Forth", "Fifth" )
val coroutineScope = rememberCoroutineScope()
val additionalPagerState = rememberPagerState()
val tabIndex = additionalPagerState.currentPage
ScrollableTabRow(
selectedTabIndex = tabIndex,
edgePadding = 16.dp,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
modifier = Modifier.pagerTabIndicatorOffset(
additionalPagerState,
tabPositions
),
color = MaterialTheme.colors.primary
)
},
backgroundColor = Color.Transparent
) {
additionalTitles.forEachIndexed { index, tabTitle ->
Tab(
modifier = Modifier
.height(
height = 30.dp
),
selected = tabIndex == index,
onClick = {
coroutineScope.launch {
additionalPagerState.animateScrollToPage(index)
}
},
text = {
Text(
modifier = Modifier
.alpha(
alpha = if (tabIndex == index) 1f else 0.85f
),
text = tabTitle,
style = MaterialTheme.typography.h6.copy(
fontSize = 14.sp,
letterSpacing = -(0.25).sp,
color = if (tabIndex == index) Color.Black else Color.Magenta
)
)
}
)
}
}
HorizontalPager(
modifier = Modifier
.background(Color.Blue),
contentPadding = PaddingValues(0.dp),
count = additionalTitles.size,
state = additionalPagerState,
userScrollEnabled = true
) { index ->
Column(
modifier = Modifier
.fillMaxSize()
) {
when (additionalTitles[index]) {
"First" -> {
for (a in 0..2) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(
16.dp
)
.background(Color.Red),
text = additionalTitles[index],
style = MaterialTheme.typography.h6
)
}
}
"Second" -> {
for (a in 0..3) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(
16.dp
)
.background(Color.Green),
text = additionalTitles[index],
style = MaterialTheme.typography.h6
)
}
}
"Third" -> {
for (a in 0..7) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(
16.dp
)
.background(Color.Green),
text = additionalTitles[index],
style = MaterialTheme.typography.h6
)
}
}
"Forth" -> {
for (a in 0..15) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(
16.dp
)
.background(Color.Green),
text = additionalTitles[index],
style = MaterialTheme.typography.h6
)
}
}
"Fifth" -> {
for (a in 0..3) {
Text(
modifier = Modifier
.fillMaxWidth()
.padding(
16.dp
)
.background(Color.Green),
text = additionalTitles[index],
style = MaterialTheme.typography.h6
)
}
}
}
}
}
}
https://user-images.githubusercontent.com/21175086/179075589-ca825df4-580d-4813-881f-f0418b48dcf4.mp4
`
Update:
I decided to create my own CustomLayout with Column behaviour
@Composable
fun CustomColumn(
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Layout(
modifier = modifier,
content = content,
measurePolicy = { measurables, constraints ->
val placeables = measurables.map { measurable ->
// Measure each children
measurable.measure(constraints)
}
var yPosition = 0
layout(constraints.maxWidth, constraints.maxHeight) {
placeables.forEach { placeable ->
placeable.placeRelative(x = 0, y = yPosition)
yPosition += placeable.height
}
}
}
)
} ```
The elements locates on the screen as needed but when on the first tab I swipe left - the app crashes with this error:
java_vm_ext.cc:579] JNI DETECTED ERROR IN APPLICATION: JNI NewStringUTF called with pending exception java.lang.IllegalStateException: Unable to create layer for Compose, size 1280x-2147483520 max size 16384 color type 4 has context 1
java_vm_ext.cc:579] (Throwable with empty stack trace)
java_vm_ext.cc:579]
java_vm_ext.cc:579] in call to NewStringUTF
2022-07-14 18:42:06.195 22821-22887/com.packagename A/roid.myappname.bet: runtime.cc:669] Runtime aborting...
runtime.cc:669] Dumping all threads without mutator lock held
runtime.cc:669] All threads:
runtime.cc:669] DALVIK THREADS (50):
runtime.cc:669] "RenderThread" prio=10 tid=50 Runnable
runtime.cc:669] | group="" sCount=0 ucsCount=0 flags=0 obj=0x14683b90 self=0x7bfeb17c70
runtime.cc:669] | sysTid=22887 nice=-10 cgrp=top-app sched=0/0 handle=0x79efca0cb0
runtime.cc:669] | state=R schedstat=( 34993437 3521875 81 ) utm=0 stm=2 core=7 HZ=100
runtime.cc:669] | stack=0x79efba9000-0x79efbab000 stackSize=991KB
runtime.cc:669] | held mutexes= "abort lock" "mutator lock"(shared held)
runtime.cc:669] native: #00 pc 0000000000458f1c /apex/com.android.art/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+120)
runtime.cc:669] native: #01 pc 00000000006f98a8 /apex/com.android.art/lib64/libart.so (art::Thread::DumpStack(std::__1::basic_ostream<char, std::__1::char_traits<char> >&, bool, BacktraceMap*, bool) const+252)
runtime.cc:669] native: #02 pc 0000000000701380 /apex/com.android.art/lib64/libart.so (art::DumpCheckpoint::Run(art::Thread*)+304) ```
Someone knows where to dig ?
Update:
Most likely the issue is related to .verticalScroll(rememberScrollState()) modifier. When I remove it the issue with extra padding is not happening but the list is not scrolling now.
I'm having the exactly same issue, but using a LazyColumn as a parent composable and HorizontalPage is an item
which contains 2 tabs with lists each, displaying list items using foreach
I think the issue could be that you have Modifier.verticalScroll(), but then your child Pager has a Column with Modifier.fillMaxSize(). you can't really have both vertical scroll and filling vertical size. So most likely the page with inner pager shouldn't be vertically scrollable, or this modifier should be set on the inner Pager's child, not the outer ones
Could you please provide an example with the workaround solution ? Because I used to try different approaches and it didn't work. If you check this example everything is working fine. https://www.geeksforgeeks.org/nested-scrolling-in-android-using-jetpack-compose/
So it means that I can set vertical scroll in inner and outer widgets.
There is no vertical nesting scrolling in your case. You only have Modifier.verticalScroll() set once. And what I am saying is that a child of a component with Modifier.verticalScroll() can't have Modifier.fillMaxHeight() as it makes no sense, verticalScroll is automatically measuring its children with an infinity max height, nothing to fill, so it will fallback to wrapping content
When I'm removing verticalScroll and fillMaxSize modifiers, I have the same padding issue but now in both viewpagers. Seems like it's something else :(
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.
It's still need to be fixed
Can you change the status of this issue to open ?
Hey. I still believe it is an issue in how you structured your code. Can you please explain the issue if you fix the one I already mentioned: when you set Modifier.verticalScroll() on a parent (Column in the first Pager) all the subhierarchy can't have Modifier.fillMaxHeight() anymore (Modifier.fillMaxSize is setting both width and height) as it can't fill the infinity