Reputation: 173
I need to make a layout like this:
The layout is: an image with fixed size and a layout with grey background that should fill the remaining space of the screen. These elements should be scrollable. Though the layout is quite simple I can't handle it. The reason is that I need to to add .weight(1f) modifier to make grey background fill the rest of the screen, but it disables scroll instead. And vice versa, without this modifier scroll works properly, but grey background doesn't fill the rest of the screen.
I suppose scroll doesn't work because it need to know size of it's content and it doesn't. But I could be wrong. Could you please help me to fix this problem?
@Composable
fun TestCompose(
text: String,
modifier: Modifier = Modifier,
) {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxSize()
.background(color = Color.White),
) {
Column(
modifier = Modifier
.verticalScroll(state = scrollState)
.weight(1f)
) {
Image(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
.align(Alignment.CenterHorizontally),
painter = painterResource(id = drawable.check_status_icon),
contentDescription = null
)
Column(
modifier = Modifier
.weight(1f) // This line makes grey background fill the rest of the screen as it is demanded, but disables scroll instead.
.clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp))
.background(Color.LightGray)
) {
// Here are different optional custom layouts inside a column but I left Text only
Text(text = text)
AnotherLayout()
}
}
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {}){
Text("Go to main", fontSize = 25.sp)
}
}
}
Btw if I use .fillMaxHeight()
modifier instead of .weight(1f)
the layout wraps content. This is behaviour I don't understand as well:
Upvotes: 5
Views: 5007
Reputation: 67209
There is nothing wrong how scroll works and assigning Modifier.weight(1f)
doesn't disable scroll, it lets content to fit parent perfectly, scroll is still being invoked but there is no place to scroll. You can verify this with scrollState.isScrollInProgress
.
When you assign Modifier.weight
Column
gets exact size that is left from Image
above. And total height of contents or child Composables of Column
with Modifier.verticalScroll()
is equal to itself and because of that you can't scroll.
If you change that sample that takes 4 Boxes with each 100.dp height
@Preview
@Composable
fun TestCompose() {
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxSize()
.background(color = Color.White),
) {
Column(
modifier = Modifier
.verticalScroll(state = scrollState)
.weight(1f)
) {
Image(
modifier = Modifier
.fillMaxWidth()
.background(Color.White)
.align(Alignment.CenterHorizontally),
painter = painterResource(id = R.drawable.avatar_1_raster),
contentScale = ContentScale.FillWidth,
contentDescription = null
)
val density = LocalDensity.current
var height by remember {
mutableStateOf(0.dp)
}
Text("Column height: $height")
Column(
modifier = Modifier
.onSizeChanged {
height = with(density) {
it.height.toDp()
}
}
.border(8.dp, Color.Black)
.weight(1f) // This line makes grey background fill the rest of the screen as it is demanded, but disables scroll instead.
.clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp))
.background(Color.LightGray)
) {
Box(modifier = Modifier.fillMaxWidth().height(100.dp).background(Color.Red))
Box(modifier = Modifier.fillMaxWidth().height(100.dp).background(Color.Yellow))
Box(modifier = Modifier.fillMaxWidth().height(100.dp).background(Color.Green))
Box(modifier = Modifier.fillMaxWidth().height(100.dp).background(Color.Blue))
}
}
Button(
modifier = Modifier.fillMaxWidth(),
onClick = {}) {
Text("Go to main", fontSize = 25.sp)
}
}
}
And based on your image and device screen size you will see something like
It tells that Column with black border fits screen completely while some of its content, Box with Blue background, is not fit because its total height is 293.dp
which is remaining space after Image
and Text
is measured, and fits completely.
If you comment weight in inner Column you will see that its total height is 400.dp and border goes below Blue box and since 400.dp is bigger than available place, it was 293.dp, you can scroll (400-293).dp
You can check this below while content of Column also equal to its height.
@Preview
@Composable
fun ScrollTest() {
val scrollState = rememberScrollState()
Column {
Text("Is scrolling: ${scrollState.isScrollInProgress}")
Column(
modifier = Modifier.fillMaxWidth().height(200.dp).border(2.dp, Color.Blue)
.verticalScroll(scrollState)
) {
Box(modifier = Modifier.fillMaxWidth().height(100.dp).background(Color.Red))
Box(modifier = Modifier.fillMaxWidth().height(100.dp).background(Color.Yellow))
// Box(modifier = Modifier.fillMaxWidth().height(100.dp).background(Color.Green))
}
}
}
then uncomment Green box and you will see that scroll
You can consider it like having a LazyColumn
that all elements exactly fits its height, do you expect it to scroll with an empty space below? For scroll to happen dimension of contents of Composable should be bigger in orientation of scroll gesture.
For instance, if your Column has 200.dp and total height of its content is 200.dp you won't be able to see its content move up or down.
It's not clear how big height should be in image on the left for it to scroll while there is no content.
Upvotes: 3
Reputation: 1266
You can check if the text has enough height to fill the empty space with below code and update the ui according to it.
var textDidOverflowHeight by remember {
mutableStateOf(false)
}
if (textDidOverflowHeight) {
Column(modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp))
.background(Color.LightGray)) {
Text(text = text)
}
} else {
Column(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp))
.background(Color.LightGray)
) {
Text(text = text,
onTextLayout = {
textDidOverflowHeight = it.didOverflowHeight
})
}
}
Upvotes: 0