Insets for Jetpack Compose
Warning
This library is deprecated, with official insets support in androidx.compose.foundation.
The migration guide and original documentation is below.
Migration
The official
androidx.compose.foundation
insets support is very similar to accompanist/insets, with a few changes.
androidx.compose.foundation
also does not disable window decor fitting, so you still need to call
WindowCompat.setDecorFitsSystemWindows(window, false)
from your Activity.
You also still need to set the system bar backgrounds to be transparent, which can be done with our
System UI Controller
library.
If you are using insets for IME support, you also still need to ensure that the activity's
windowSoftInputMode
is set to
adjustResize
:
<activity
android:name=
".MyActivity"
android:windowSoftInputMode=
"adjustResize"
>
</activity>
Migration steps:
- Remove
ProvideWindowInsets
(there is no equivalent in
androidx.compose.foundation
)
- Remove
ViewWindowInsetObserver
(there is no equivalent in
androidx.compose.foundation
)
- Replace padding modifiers with
androidx.compose.foundation
equivalents. If using
additionalPadding
or only applying the insets to certain sides, use the corresponding
WindowInsets.add
and
WindowInsets.only
extensions.
- Replace
rememberInsetsPaddingValues
with the equivalent
WindowInsets.asPaddingValues
.
- Replace direct calculations from
LocalWindowInsets.current
with calculations on
WindowInsets
.
- Continue using the non-deprecated
insets-ui
for now.
For reference, consult the
Migration table
below.
Inset consumption
The biggest behavioral change between
accompanist/insets
and
androidx.compose.foundation
is in the consumption behavior of padding modifiers.
In
accompanist/insets
, the padding modifiers always padded the full size of the specified inset types, which led to some unintuitive duplicate padding when nesting modifiers.
For example, let’s look at what happens when we have nested boxes, where the outer one has Modifier.systemBarsPadding() applied, and the inner has Modifier.imePadding():
Box
(
Modifier
.
systemBarsPadding
())
{
Box
(
Modifier
.
imePadding
())
{
// content
}
}
Let’s assume that the bottom system bar padding is
30dp
, to account for the navigation bar padding, and let’s assume that when the IME is visible, the height of the IME is
150dp
.
When the IME is closed, the outer box will apply the bottom
30dp
as padding, and the inner box will apply zero additional padding, since the IME isn’t visible.
When the IME opens, the outer box will continue to apply the bottom
30dp
as the system bar padding, and the inner box will now apply
150dp
bottom padding, since that is the full height of the IME.
This results in a total padding of
180dp
applied to the content, which double pads the bottom navigation bar padding.
The solutions to this issue were using
derivedWindowInsetsTypeOf
, built-in derived types like
Modifier.navigationBarsWithImePadding()
, or performing calculations manually to apply the remaining padding.
In
androidx.compose.foundation
, when the IME is open, the outer box still apply the bottom
30dp
, but the inner box will only apply the remaining
120dp
needed to have the content be padded a total of
150dp
to match the height of the IME.
This behavior can be influenced further in
androidx.compose.foundation
with
Modifier.consumedWindowInsets()
As a result, the equivalent of
Modifier.navigationBarsWithImePadding()
is simply
Modifier.navigationBarsPadding().imePadding()
.
Migration table:
Original docs
Insets for Jetpack Compose takes a lot of the ideas which drove
Insetter
for views, and applies them for use in composables.
Usage
To setup Insets in your composables, you need to call the
ProvideWindowInsets
function and
wrap your content. This would typically be done near the top level of your composable hierarchy:
setContent
{
MaterialTheme
{
ProvideWindowInsets
{
// your content
}
}
}
ProvideWindowInsets
allows the library to set an
OnApplyWindowInsetsListener
on your content's host view. That listener is used to update the value of a composition local bundled in this library:
LocalWindowInsets
.
LocalWindowInsets
holds an instance of
WindowInsets
which contains the value of various
WindowInsets
types
. You can use the values manually like so:
@Composable
fun
ImeAvoidingBox
()
{
val
insets
=
LocalWindowInsets
.
current
val
imeBottom
=
with
(
LocalDensity
.
current
)
{
insets
.
ime
.
bottom
.
toDp
()
}
Box
(
Modifier
.
padding
(
bottom
=
imeBottom
))
}
...but we also provide some easy-to-use
Modifier
s.
Modifiers
We provide two types of modifiers for easy handling of insets: padding and size.
Padding modifiers
The padding modifiers allow you to apply padding to a composable which matches a specific type of inset. Currently we provide:
These are commonly used to move composables out from under the system bars. The common example would be a
FloatingActionButton
:
FloatingActionButton
(
onClick
=
{
/* TODO */
},
modifier
=
Modifier
.
align
(
Alignment
.
BottomEnd
)
.
padding
(
16.
dp
)
// normal 16dp of padding for FABs
.
navigationBarsPadding
()
// Move it out from under the nav bar
)
{
Icon
(
imageVector
=
Icons
.
Default
.
Add
,
contentDescription
=
null
)
}
Size modifiers
The size modifiers allow you to match the size of a composable to a specific type of inset. Currently we provide:
These are commonly used to allow composables behind the system bars, to provide background protection, or similar:
Spacer
(
Modifier
.
background
(
Color
.
Black
.
copy
(
alpha
=
0.7f
))
.
statusBarsHeight
()
// Match the height of the status bar
.
fillMaxWidth
()
)
PaddingValues
Compose also provides the concept of
PaddingValues
, a data class which contains the padding values to be applied on all dimensions (similar to a rect). This is commonly used with container composables, such as
LazyColumn
, to set the content padding.
You may want to use inset values for content padding, so this library provides the
rememberInsetsPaddingValues()
extension function to convert between
Insets
and
PaddingValues
. Here's an example of using the system bars insets:
LazyColumn
(
contentPadding
=
rememberInsetsPaddingValues
(
insets
=
LocalWindowInsets
.
current
.
systemBars
,
applyTop
=
true
,
applyBottom
=
true
,
)
)
{
// content
}
For a more complex example, see the
EdgeToEdgeLazyColumn
example:
Inset-aware layouts (
insets-ui
)
Warning
This library is deprecated, with
official support
in androidx.compose.material.
The original documentation is below.
Unfortunately, most of Compose Material's layouts do not support the use of content padding, which means that the following code probably doesn't produce the effect you want:
// ?? This likely doesn't do what you want
TopAppBar
(
// content
modifier
=
Modifier
.
statusBarsPadding
()
)
To workaround this, we provide the
insets-ui
companion library which contains versions of commonly used layouts, with the addition of a
contentPadding
parameter. The example below is using our
TopAppBar
layout, providing the status bar insets to use as content padding:
import
com.google.accompanist.insets.ui.TopAppBar
TopAppBar
(
contentPadding
=
rememberInsetsPaddingValues
(
insets
=
LocalWindowInsets
.
current
.
statusBars
,
applyStart
=
true
,
applyTop
=
true
,
applyEnd
=
true
,
)
)
{
// content
}
The library also provides a modified copy of Compose Material's
Scaffold
which better supports edge-to-edge layouts, by drawing the top and bottom bars over the content.
Scaffold
(
topBar
=
{
// We use TopAppBar from accompanist-insets-ui which allows us to provide
// content padding matching the system bars insets.
TopAppBar
(
title
=
{
Text
(
stringResource
(
R
.
string
.
insets_title_list
))
},
backgroundColor
=
MaterialTheme
.
colors
.
surface
.
copy
(
alpha
=
0.9f
),
contentPadding
=
rememberInsetsPaddingValues
(
LocalWindowInsets
.
current
.
statusBars
,
applyBottom
=
false
,
),
)
},
bottomBar
=
{
// We add a spacer as a bottom bar, which is the same height as
// the navigation bar
Spacer
(
Modifier
.
navigationBarsHeight
().
fillMaxWidth
())
},
)
{
contentPadding
->
// We apply the contentPadding passed to us from the Scaffold
Box
(
Modifier
.
padding
(
contentPadding
))
{
// content
}
}
See the
API docs
for a list of the other layouts provided in the library.
Animated Insets support
The library now has experimental support for
WindowInsetsAnimations
, allowing your content is react to inset animations, such as the on screen-keyboard (IME) being animated on/off screen. The
imePadding()
and
navigationBarsWithImePadding()
modifiers are available especially for this use-case.
This functionality works wherever
WindowInsetsAnimationCompat
works, which at the time or writing is on devices running API 21+.
To enable animated insets support, you need need to new
ProvideWindowInsets
overload, and set
windowInsetsAnimationsEnabled = true
.
ProvideWindowInsets
(
windowInsetsAnimationsEnabled
=
true
)
{
// content
}
You can then use the new
navigationBarsWithImePadding()
modifier like so:
OutlinedTextField
(
// other params,
modifier
=
Modifier
.
navigationBarsWithImePadding
()
)
See the
ImeAnimationSample
for a working example.
IME animations
If you're using the animation insets support for IME/keyboard animations, you also need to ensure that the activity's
windowSoftInputMode
is set to
adjustResize
:
<activity
android:name=
".MyActivity"
android:windowSoftInputMode=
"adjustResize"
>
</activity>
The default value of
windowSoftInputMode
should
work, but Compose does not currently set the flags necessary (see
here
).
?? Experimental
The features below are experimental, and require developers to
opt-in
.
Controlling the IME (on-screen keyboard)
Download
repositories
{
mavenCentral
()
}
dependencies
{
implementation
"com.google.accompanist:accompanist-insets:<version>"
// If using insets-ui
implementation
"com.google.accompanist:accompanist-insets-ui:<version>"
}
Snapshots of the development version are available in
Sonatype's
snapshots
repository
. These are updated on every commit.
Something not working?
If you find that something isn't working correctly, here's a checklist to try:
- Check that you've called
WindowCompat.setDecorFitsSystemWindows(window, false)
in your Activity. Unless you do that, the window decor will consume the insets, and they will not be dispatched to your content.
- If it's something related to the keyboard, check that the Activity's
windowSoftInputMode
is set to
adjustResize
. Without that, IME visibility changes will not be sent as inset changes.
- Similarly, if you're setting
android:windowFullscreen
to
true
(or using a
.Fullscreen
theme), be aware that
adjustResize
will not work. Please see the
documentation
for an alternative.
- If you're using
ProvideWindowInsets
(or
ViewWindowInsetObserver
) in multiple layers of your view hierarchy (i.e. in the activity,
and
in a fragment), you need to turn off consuming of insets. By default
ProvideWindowInsets
and
ViewWindowInsetObserver
will completely consume any insets passed to it. In the previous example, this means that the activity content will get the insets, but the fragment won't. To disable consuming, pass
consumeWindowInsets = false
to
ProvideWindowInsets
or
ViewWindowInsetObserver.start()
.