Search code examples
androidandroid-jetpack-composematerial-design

Scaffold with Card containing LazyColumn doesn't scroll


My screen is in a Scaffold containing a Card with a nested LazyColumn. If the size of the LazyColumn items exceeds the screen height, the Card is not completely scrollable.

Is this a bug or am I doing something wrong?

ScrollableScreenTheme {
    val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
    Scaffold(
        modifier = Modifier
            .fillMaxSize()
            .nestedScroll(scrollBehavior.nestedScrollConnection),
        topBar = {
            TopAppBar(
                title = { Text("TopAppBar Title") },
                scrollBehavior = scrollBehavior
            )
        }
    ) { innerPadding ->
        MyScaffoldContent(Modifier.padding(innerPadding))
    }
}

@Composable
fun MyScaffoldContent(
    modifier: Modifier
) {
    Column (modifier = modifier) {
        Card {
            val items = Array(20) { i -> "Item: $i"  }   // change the Array size until the screen is filled, then the buttons are not shown 
            LazyColumn {
                items(items = items, key = {it}) {
                    Text(it)
                }
            }
            Button(onClick = {}) {
                Text("Click me")
            }
        }
        Button(onClick = {}) {
            Text("Click me again")
        }
    }
}

Solution

  • Column works in such a way that it measures its children in the order they are defined and offers them as much height as is currently available. Your LazyColumn then claims all of it for itself and there is nothing left for the Buttons.

    If you want to change this, use the Modifier.weight, its documentation says:

    Size the element's height proportional to its weight relative to other weighted sibling elements in the Column. The parent will divide the vertical space remaining after measuring unweighted child elements and distribute it according to this weight. When fill is true, the element will be forced to occupy the whole height allocated to it. Otherwise, the element is allowed to be smaller - this will result in Column being smaller, as the unused allocated height will not be redistributed to other siblings.

    So with this, Buttons will be measured first, and only the remaining space will be offered to LazyColumn. If you don't want the LazyColumn to occupy the whole height event when it has fewer items, set also fill = false on both of the modifiers.

    Column (modifier = modifier) {
      Card(modifier = Modifier.weight(1F)) {
        LazyColumn(modifier = Modifier.weight(1F)) {
          ...
        }
        Button(onClick = {}) {
          Text("Click me")
        }
      }
      Button(onClick = {}) {
        Text("Click me again")
      }
    }