I know that the TopAppBar
in Material 3 has a Left/right padding of 16dp. However I am wondering where in the source code that is defined? For example, the TopAppBar constructor looks like this - no hint to the default left/right padding.
@ExperimentalMaterial3Api
@Composable
fun TopAppBar(
title: @Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: @Composable () -> Unit = {},
actions: @Composable RowScope.() -> Unit = {},
windowInsets: WindowInsets = TopAppBarDefaults.windowInsets,
colors: TopAppBarColors = TopAppBarDefaults.topAppBarColors(),
scrollBehavior: TopAppBarScrollBehavior? = null
) {
SingleRowTopAppBar(
modifier = modifier,
title = title,
titleTextStyle = MaterialTheme.typography.fromToken(TopAppBarSmallTokens.HeadlineFont),
centeredTitle = false,
navigationIcon = navigationIcon,
actions = actions,
windowInsets = windowInsets,
colors = colors,
scrollBehavior = scrollBehavior
)
}
Is there is any constant I can use to apply this padding to other components or do I have to hardcode the 16dp? I want the things below the TopAppBar
to have the same left/right padding, otherwise it does not look consistent.
It is defined further down the call chain inside the TopAppBarLayout
composable, inside the titlePlaceable.placeRelative
section.
Here are the relevant code sections
Box(
Modifier
.layoutId("navigationIcon")
.padding(start = TopAppBarHorizontalPadding) // <-- line of interest
) {
CompositionLocalProvider(
LocalContentColor provides navigationIconContentColor,
content = navigationIcon
)
}
Box(
Modifier
.layoutId("title")
.padding(horizontal = TopAppBarHorizontalPadding) // <-- line of interest
// ...
) {
ProvideTextStyle(value = titleTextStyle) {
CompositionLocalProvider(
LocalContentColor provides titleContentColor.copy(alpha = titleAlpha),
content = title
)
}
}
layout(constraints.maxWidth, layoutHeight) {
// Navigation icon
navigationIconPlaceable.placeRelative(
x = 0,
y = (layoutHeight - navigationIconPlaceable.height) / 2
)
// Title
titlePlaceable.placeRelative(
x = when (titleHorizontalArrangement) {
Arrangement.Center -> (constraints.maxWidth - titlePlaceable.width) / 2
Arrangement.End ->
constraints.maxWidth - titlePlaceable.width - actionIconsPlaceable.width
// Arrangement.Start.
// An TopAppBarTitleInset will make sure the title is offset in case the
// navigation icon is missing.
else -> max(TopAppBarTitleInset.roundToPx(), navigationIconPlaceable.width)
},
// ...
And this are the values used in the code above
private val TopAppBarHorizontalPadding = 4.dp
private val TopAppBarTitleInset = 16.dp - TopAppBarHorizontalPadding
These are defined as part of the library so they are basically constants, and are unlikely to change between versions. Even if they ever change, you have control over when to update the library and at that point you would also update your paddings to match again.
So when the top app bar does not display a navigation icon, the padding at the start
side is 12.dp
and then the title Box
follows, which has its own padding of 4.dp
for a total of 16.dp
of padding.
And when the top bar does display a navigation icon the padding at the start
side is 0.dp
then the navigation icon Box
follows, which has its own padding of 4.dp
for a total of 4.dp
of padding.
However, usually the navigation icons are added by using an IconButton
and adding an Icon
as content to it. The minimum (touch) size of the IconButton
is 48.dp
(defined inside the IconButton
composable) and the default Icon
size is 24.dp
. This means that even though the actual padding is 4.dp the "visual" padding up to the side of the Icon
is 4.dp + (48.dp / 2 - 24.dp / 2) = 4.dp + 12.dp
= 16.dp
.
It is similar on the end
side where the action icons are or where the title ends (when there are no action icons).
So in both cases (as long as you are using default icon sizes) the "visual" paddings are the same at 16.dp
. So if you decide to go with a padding of 16.dp
for your content the layouts will look aligned.
Note, that all this is assuming that the left/right windowInsets
are 0, which might not be the case on all devices and all orientations.
So to ensure that the layouts look aligned in all cases you also need to take windowInsets
into consideration in your layout. If you are already, then things should align correctly when you add 16.dp
of padding. If you are not, then you either need to apply windowInsets
to you (parent) composable (this should be the easier option) or you need to add the windowInsets
to the calculations for the correct side based on LayoutDirection
by calling calculateLeftPadding
and calculateRightPadding
.
val ld = LocalLayoutDirection.current
val insets = windowInsets.asPaddingValues(LocalDensity.current)
val leftPadding = insets.calculateLeftPadding(ld)
val rightPadding = insets.calculateRightPadding(ld)