32
@Composable
fun LayoutsCodelab() {
    Scaffold(
        topBar = {
            TopAppBar(
                title = {
                    Text(text = "LayoutsCodelab")
                }
            )
        }
    ) { innerPadding ->
        BodyContent(Modifier.padding(innerPadding))
    }
}

Removing innerPadding does not change anything.

Please note, I am new to Compose.
if you have any confusion, please ask in comments

Johann
  • 27,536
  • 39
  • 165
  • 279
Curious Learner
  • 1,182
  • 1
  • 4
  • 17

2 Answers2

46

The padding values provided to your content are only set if you set the bottomBar parameter. This unfortunately is not documented. If you set the bottomBar, the Scaffold will set the bottom padding of PaddingValues to be the height of the content that the bottomBar content uses. The other padding values (start, top, end) are currently set to zero. Not sure if that will change in the future.

Providing the bottom padding is useful if your content contains a scrollable list and you want to offset the bottom of the list by the height of the bottomBar content. If you don't include any bottomBar content, the PaddingValues will always remain set to zero.

Here is an example that shows how the PaddingValues parameter is passed into your content when the bottomBar uses a BottomAppBar. It displays a list of 50 items. When the padding is added to the Column, you can scroll to the bottom and see the last item. If you remove the Column's padding and repeat the test, when you scroll to the bottom, the last several items are not visible because the Column extends all the way to the bottom of the scaffold without any bottom padding and the bottom app bar covers those last items:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        startActivity(intent)

        setContent {

            Scaffold(
                topBar = {
                    TopAppBar(
                        title = {
                            Text(text = "Padding values in a scaffold")
                        }
                    )
                },
                bottomBar = {
                    BottomAppBar() {
                        CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
                            IconButton(onClick = { /* doSomething() */ }) {
                                Icon(Icons.Filled.Menu, contentDescription = "Localized description")
                            }
                        }
                        Spacer(Modifier.weight(1f, true))
                        IconButton(onClick = { /* doSomething() */ }) {
                            Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
                        }
                        IconButton(onClick = { /* doSomething() */ }) {
                            Icon(Icons.Filled.Favorite, contentDescription = "Localized description")
                        }
                    }
                }
            ) { paddingValues ->
                Column(modifier = Modifier
                    .fillMaxSize()
                    .padding(bottom = paddingValues.calculateBottomPadding())
                    .verticalScroll(rememberScrollState())) {

                    Text("Bottom app bar padding:  $paddingValues")

                    repeat(50) {
                        Text(it.toString())
                    }
                }
            }
        }
    }
}

I can't understand why Google would not include the top padding. After all, if the top app bar is included, it also takes up space. I think the reason has to do with collapsing top bars. If the top app bar collapses while scrolling, the content area needs to expand - so adding padding to the top may not make sense. It does make sense though if the top app bar remains fixed.

Johann
  • 27,536
  • 39
  • 165
  • 279
  • Are you telling that, if there is a bottom-App-Bar, then the content above it will automatically calculate and adjust required bottom-padding – Curious Learner Dec 03 '21 at 11:13
  • what is the difference between `innerPadding` and `paddingValues`? I asked in the question about `innerPadding`. when to use `innerPadding` and when to use `paddingValues`? – Curious Learner Dec 03 '21 at 11:18
  • 1
    If you provide content to the bottomBar parameter, the Scaffold will calculate the height of that content and return that height in the PaddingValues - whhich you can retrieve by calling calculateBottomPadding(). The scaffold doesn't calculate the height of the content above the bottom bar but rather the content you provide to the bottomBar parameter. The "innerPadding" is the same thing as the "paddingValues" variable I am using. You just happened to have named yours "innerPadding". You can name the parameter to whatever you want - that's the way Kotlin lambdas work. – Johann Dec 03 '21 at 11:25
  • @Johaan ok understood... And as you said, this is only applicable for bottom app-bar and not for top app-bar, if I have got it right – Curious Learner Dec 03 '21 at 11:32
  • That is correct. Just keep in mind that the bottomBar doesn't have to contain an AppBottomBar composable. It can contain whatever you want, although the AppBottomBar is the most used widget used at the bottom. Nothing in the topBar is included in the paddingValues, which is kind of a shame. If you found my solution useful, please mark it as accepted. Thanks! – Johann Dec 03 '21 at 11:41
  • @Johaan Can you help me with this? [Link](https://stackoverflow.com/q/70240641/17123424) – Curious Learner Dec 06 '21 at 03:51
  • 2
    I think top padding is now included in this parameter – Arthur Khazbs Aug 15 '22 at 21:09
  • @ArthurKhazbs no it's not, at least not on the latest alpha 1.4.0-alpha04: Lines 32 to 323: `val bodyContentPlaceables = subcompose(ScaffoldLayoutContent.MainContent) { val innerPadding = PaddingValues(bottom = bottomBarHeight.toDp()) content(innerPadding) }.fastMap { it.measure(looseConstraints.copy(maxHeight = bodyContentHeight)) }` – m.reiter Jan 19 '23 at 10:36
3

As per documentation

content lambda receives an instance of PaddingValues that should be applied to the content root — for example, via Modifier.padding — to offset the top and bottom bars, if they exist.

AndroidSam27
  • 238
  • 2
  • 11