Is it expected that `WindowInsets.isImeVisible` re...
# compose
e
Is it expected that
WindowInsets.isImeVisible
returns false when the keyboard is showing if the softInputMode is set to pan?
a
Yes, I would expect that it might return false on some API versions, since
adjustPan
can cause changes that the app isn’t fully aware of.
adjustResize
gives the most consistent results across API versions, as it gives the most information to the app about how the insets are changing
e
Isn't
adjustResize
deprecated though? Does it make sense to only use it until the API version it was deprecated in, and then switch to
adjustPan
?
a
Yes that is true, and that also lines up with https://issuetracker.google.com/issues/176400965 where API 30 is the version where the insets should be available with
adjustPan
. For simplicity I usually just specify
adjustResize
only in the manifest, but you should be able to apply
adjustResize
for API 29 and below, and
adjustPan
for API 30 and above. I’d actually be very interested to know if that works if you try it out!
e
I'm trying out a few different options. I'll add that to the matrix and report back 😁
So for API 29 and below if I use
adjustResize
then
WindowInsets.isImeVisible
works correctly, but
TextField
isn't properly scrolled into view when it gets focus. If I use
adjustPan
then the
TextField
is properly scrolled into view when it gets focus, but
WindowInsets.isImeVisible
doesn't work correctly.
a
For the first case of
adjustResize
, are you changing the size of the scrollable container itself based on the IME insets?
e
Not in this particular case. I'll try to get a screen capture of what I'm doing, together with some code. We're using
WindowInsets.isImeVisible
to drive treatment of a
BasicTextField
decorationBox. When the IME is visible we draw a border around the area that the next character will display.
Well I guess that actually does change the size of the scrollable container by (~1 dp) 🤔
a
If you’re handling insets directly with
WindowCompat.setDecorFitsSystemWindows(window, false)
, then you’ll want to ensure you are applying insets to your components to avoid overlapping. But it sounds like you might want to have logic based on whether the text field is focused? If you’re using a hardware keyboard, you might want to have the border visible as well?
e
We're in middle of converting a large codebase from View to Compose and we didn't really look at that yet. Our theme sets
android:fitsSystemWindows
to
false
and we're using the
Scaffold
composable at the top level of all of our screens (single activity)
I tried changing
android:fitsSystemWindows
to
true
and used
adjustResize
(I didn't add
WindowCompat.setDecorFitsSystemWindows(window, false)
) and that seems to be working on all API levels, where
WindowInsets.isImeVisible
is working correctly, and the container is scrolling correctly to show the text field.
a
Nice! My guess is that the
fitsSystemWindows
is providing the inset handling, but since the app is getting notified more than with
adjustPan
,
WindowInsets.isImeVisible
is still giving the correct value. If you want the nice synchronized IME animations, or to go edge-to-edge with more fine-tuned behavior, then you’ll probably want to turn
fitSystemWindows
back to
false
, and handle the insets directly at a per screen level (using
imePadding
and other modifiers) But glad you found a solution that works for you as you are converting.
e
Well it works for this particular use case. The rest of the app is not taking it so well 😅 It's a good starting point though, thanks!
I started checking with the debugger to see how frequently the app is getting notified with
adjustPan
and it looks like it doesn't get notified at all on API 29 and below. Is that expected?
Seems like I'm playing a game of whack a mole, and every time I make progress something else breaks 🙈 Using
adjustPan
and
fitSystemWindows=false
I was able to get scrolling and
isImeVisible
working correctly on API 29 and below by setting
windowTranslucentStatus
and
windowTranslucentNavigation
to
false
(they were both
true
previously). However, now screens that aren't using Compose aren't handling insets correctly (i.e. there's padding added to the views). To fix that I call
WindowCompat.setDecorFitsSystemWindows(window, false)
in my Activity, but that causes
isImeVisible
to not work correctly again.
a
Can I go back to the question of
But it sounds like you might want to have logic based on whether the text field is focused? If you’re using a hardware keyboard, you might want to have the border visible as well?
If you’re just using
isImeVisible
for that behavior
e
I'm just an engineer I don't have a say in these things 😬 It's a good point though, and I'm going to look into providing a better UX in that scenario. However we do have other places in the app that uses the equivalent of
isImeVisible
so I'd rather bite the bullet now and fix it before it becomes a problem
a
I would say you do, or at least you can ask if showing the border is because the user is inputting text, or because the software keyboard is open. The software keyboard is the most common way to do so, but not the only way. It’s also possible for the software keyboard to be open (on the device), but not overlap with your app at all in multi-window mode, in which case
isImeVisible
will likely be false. But the user could still be inputting text.
e
I'm just an engineer I don't have a say in these things 😬
That was just my lame attempt at humor. I'm more concerned with the latter point that I raised.
a
As for the whack-a-mole with inset behavior,
adjustResize
for API 29 and below with
WindowCompat.setDecorFitsSystemWindows(window, false)
will give your app the most information in the long term, although then you will need to be sure to avoid overlapping issues and handle insets directly. I wouldn’t expect
adjustPan
to work reliably on API 29 and below as per https://issuetracker.google.com/issues/176400965
s
Isn’t
adjustResize
deprecated though
Is that true? I’ve always see this as being the de-facto way to get the best kind of backwards compatibility when using that +
WindowCompat.setDecorFitsSystemWindows(window, false)
. Is there any case where one would not want to use that? If thy were targeting minSdk = 30+ for example? I wonder if there’s any material in the docs regarding picking the right choice for you, among adjustResize and its alternatives
a
It is deprecated on API 30 and above, but specifying it anyway in the manifest to get that behavior for API 29 and below along with
WindowCompat.setDecorFitsSystemWindows(window, false)
is the simplest and most consistent way to get good behavior. (Once your minSdk is API 30, you could probably switch to
adjustNothing
but I haven’t tested that in detail. And setting it dynamically instead of in the manifest in the meantime can be troublesome and quirky)
e
Off the bat
adjustNothing
on API 30+ doesn't scroll to the focused TextField
a
I think that would be because you aren’t resizing the scrolling views with
imePadding()
or similar? To go edge-to-edge, get good IME inset animations, and the most consistent
isImeVisible
value, and want the most consistent behavior across API versions, the setup to go with is: •
adjustResize
in manifest •
WindowCompat.setDecorFitsSystemWindows(window, false)
in
Activity.onCreate
Once you’re handling insets yourself completely, it is up to you to handle resizing scrolling views so that elements remain visible (Compose
1.4.0
has improvements there for
LazyColumn
in particular) In some sense, the most reliable answers to
isImeVisible
are available only when you are going edge-to-edge and the app is handling all insets, instead of the platform.
e
I am using
imePadding
in this case, together with the setup you specified.
In this video I'm using: •
adjustResize
WindowCompat.setDecorFitsSystemWindows(window, false)
in
Activity.onCreate
imePadding()
on the text field and this device is on API 33 (for now I stopped using
isImeVisible
just for simplicity)
c
I experienced similar things like in this video with the exact same setup. Im interested in how to solve this precisely? And it can happen too, that the textfield is not on the bottom of the screen
a
Which Compose version are you using? And which type of scrolling container?
e
I'm using Chris Banes' alpha bom, so these versions - https://github.com/chrisbanes/compose-bom/blob/v2023.02.00-beta03/bom/build.gradle.kts The container is a
Column
with
verticalScroll
that lives in the
content
of a
Scaffold
I tried updating to the latest alpha bom and the result is the same
a
Thanks for checking with the latest alpha bom, one more question: to which component are you applying the
Modifier.imePadding
?
e
To the
TextField
(those boxes are all one `BasicTextField`; we're using a decorationBox to achieve that look)
a
Could you see what happens when adding it to the
Column
instead?
I think it should be right before the
Modifier.verticalScroll
that’s there
e
That works!
And on top of that
isImeVisible
is working as well
a
Woohoo, nice! I’d also recommend upgrading to
1.10.0-rc01
of
androidx.core
for getting better IME animations on API 29 and below as well
It’s important that the padding is applied in such a way to change the size of the scrolling component itself, so that it knows that it shrunk and therefore needs to keep something in view. With the latest
1.4.0
releases,
LazyColumn
should work the same way, if the
LazyColumn
itself becomes smaller. (
contentPadding
doesn’t work though with that though, that’s a known issue still)
e
So
LazyColumn
needs
imePadding
as well?
a
Right, exactly.
e
OK cool. Thanks so much for the help. And this solution is great because I just have to make one change in a centralized component for all of my screens 😁
a
You are welcome, thanks for working through it!
e
Would you recommend using
adjustNothing
for API 30+ or stick with
adjustResize
?
a
I’d stick with
adjustResize
in the manifest for all API versions
650 Views