I have an Issue with `WindowInsets` in `BottomApp...
# compose
s
I have an Issue with
WindowInsets
in
BottomAppBar
. The
WindowInsets
padding are set after the transition to the new screen finishes and that results in a jump of the bottom bar. Is there a work arround for that? (video in thread)
Copy code
Scaffold(topBar = {
            ...
        }, bottomBar = {
            // Wrap the navigation bar in a surface so the color behind the system
            // navigation is equal to the container color of the navigation bar.
            Surface(
                color = MaterialTheme.colorScheme.surface,
                tonalElevation = 6.dp
            ) {
                BottomAppBar(
                    modifier = Modifier
                        .windowInsetsPadding(
                            WindowInsets.safeDrawing.only(
                                WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
                            )
                        ),
s
Not sure which navigation approach you’re using, maybe that matters into why the insets are not there until the entire navigation is done and you’re in the new screen. As a workaround maybe I’d apply a .animatedContentSize modifier in my BottomAppBar and at least have the insets come more fluidly. But that doesn’t solve the problem of the insets not being there from the start of course. Maybe @Alex Vanyo has an idea here?
Maybe somehow it’s not receiving the insets since it sees that this inset is already consumed as some node higher up in the tree. Not sure what is considered above the BottomBar since it’s inside the scaffold and I’m not sure how the hierarchy goes inside there. Could you try instead to use the inset APIs that don’t respect consumed insets and simply applies them even if they’re consumed? I don’t remember which ones can do that, but I am looking 👀
Can you edit
Copy code
BottomAppBar(
    modifier = Modifier
        .windowInsetsPadding(
            WindowInsets.safeDrawing.only(
                WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
            )
        ),
to be
Copy code
BottomAppBar(
    modifier = Modifier
        .consumedWindowInsets(WindowInsets(0, 0, 0, 0))
        .windowInsetsPadding(
            WindowInsets.safeDrawing.only(
                WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
            )
        ),
I don’t think this is it, but worth a try
No… I think
consumedWindowInsets
is to consume more than are already consumed, not to un-consume them 😅
What if you pass
contentWindowInsets = WindowInsets(0, 0, 0, 0)
on your Scaffold so that it by itself is not consuming anything and lets the children deal with it themselves? And this way they won’t be considered consumed until you reach the next destination.
s
I’ll try it in 15min.
contentWindowInsets = WindowInsets(0, 0, 0, 0)
fyi. this version didn’t work…
s
Yeah I guess it’s due to this determining what insets the Scaffold
content
is going to get, but the bottomBar is not the content lambda, it’s outside of that and those insets were consumed already at that point. I think this is intended then, if you were consuming them there well then the system considers them consumed, it can’t know you want them unconsumed during the animation. Better to let each screen handle them themselves. Looking really good then! So would you say you got into all this trouble due to you manually consuming the insets somewhere really high up the tree? How did that look like exactly and why did you do that in the first place? I’m just trying to understand myself too to make sure not to do the same mistake in the future 😅
s
So I develop Android Apps since 1.5 and I needed a small project to start with “clean architecture” and “compose” because I never did that before. I search for the best example App and started with “NowInAndroid” App. Removed all I didn’t need and started to develop my use case. Because I’m a Compose Beginner, I probably didn’t understand it correctly. The Scaffold that contains the NavHost (and therefore all content), has a Navigation Bottom Bar. That I hide on screens that are not on Top Level. In my case the “detail screen” content is on a lower level and the Navigation bar is hidden. The “detail screen” itself has another Scaffold because it needs a BottomBar (Not sure if this is the best practice) In the NowInAndroidApp the NavHost consumes the Insets, that’s why I still had it in my code as well…
… by the way I just upgraded compose from
Copy code
1.3.0-beta03
to
Copy code
1.3.0-rc1
and I get now an Exception when filtering my List content. When I downgrade it works fine 🙉 Are you an expert in LazyGrid as well? @Stylianos Gakis
Copy code
java.lang.IllegalArgumentException: Failed requirement.
                                                                                                    	at androidx.compose.foundation.lazy.grid.LazyGridSpanLayoutProvider.getLineIndexOfItem--_Ze7BM(LazyGridSpanLayoutProvider.kt:174)
                                                                                                    	at androidx.compose.foundation.lazy.grid.LazyGridItemPlacementAnimatorKt.firstIndexInNextLineAfter(LazyGridItemPlacementAnimator.kt:495)
                                                                                                    	at androidx.compose.foundation.lazy.grid.LazyGridItemPlacementAnimatorKt.access$firstIndexInNextLineAfter(LazyGridItemPlacementAnimator.kt:1)
                                                                                                    	at androidx.compose.foundation.lazy.grid.LazyGridItemPlacementAnimator.calculateExpectedOffset-xfIKQeg(LazyGridItemPlacementAnimator.kt:383)
                                                                                                    	at androidx.compose.foundation.lazy.grid.LazyGridItemPlacementAnimator.onMeasured(LazyGridItemPlacementAnimator.kt:160)
                                                                                                    	at androidx.compose.foundation.lazy.grid.LazyGridMeasureKt.measureLazyGrid-0cYbdkg(LazyGridMeasure.kt:241)
                                                                                                    	at androidx.compose.foundation.lazy.grid.LazyGridKt$rememberLazyGridMeasurePolicy$1$1.invoke-0kLqBqw(LazyGrid.kt:334)
                                                                                                    	at androidx.compose.foundation.lazy.grid.LazyGridKt$rememberLazyGridMeasurePolicy$1$1.invoke(LazyGrid.kt:184)
                                                                                                    	at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$1$2$1.invoke-0kLqBqw(LazyLayout.kt:71)
                                                                                                    	at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$1$2$1.invoke(LazyLayout.kt:69)
                                                                                                    	at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:591)
s
Aha you’re referring to this line, interesting! Yeah I assume this is marked as consumed there so that since there’s some padding already applied, if you were to apply the insets on top of it too then it’d pad too much. So force-consuming them solves this. What an interesting interaction here tbh. I’m still kinda lost on where everything is applied since I can’t see your entire code-base, but yeah. Now that you’ve removed the
consumedWindowInsets
there, you may find some trouble of over-applying some spacings if you’re using that padding and insets on the same place (like say on the bottom) since it’ll be (insets + this padding) instead of (maxOf(insets, padding)) if that makes sense 😅 And unforuntately no, I haven’t used LazyGrid myself. Just make sure your keys are correct, and try and put simple Box{} as items to see if it at least works with a dead simple situation, this is my best advice 😅
And just in general, you probably don’t want the Scaffold inside the Scaffold. In fact you may not even need the first scaffold if you don’t want to use its snackbar etc. I replicated this behavior that NiA has with something like this instead (rough idea, without most parameters):
Copy code
Column {
  Row(Modifier.weight(1f)) {
    if (appState.shouldShowNavRail) {
      NiANavRail(Modifier.safeDrawingPadding())
    }
    NiANavHost(Modifier.fillMaxHeight().weight(1f))
  }
  if (appState.shouldShowBottomBar) {
    NiABottomBar()
  }
}
And it’s the same effect, as long as you don’t need some of the Scaffold things you get as I said above. It might simplify your app to do the same, and handle insets manually instead everywhere. Relevant discussion here
s
@Stylianos Gakis you’re right again, LazyGrid problem won’t happen if I remove the
key
(something I have to look into later, the pagingdata is updated and the data is null, therefore I don’t have the right key, so it crashes I think…) Yes exactly you found the line 👌 Seems like I wouldn’t need the Scaffold now. I just thought it’s best practice to use it. Snackbars… And I thought maybe there is something similar like in the View system
Copy code
app:layout_behavior
where you can specify some magic and the bottombar/topbar disappears when you scroll down a list… I have another issue with Inset padding (but not related to this issue I think). In my ModalBottomSheet the Inset is added twice when the IME appears until a recompose happens I don’t know if you up for it, but I can give you access to my code or we can have a huddle with screenshare, if you have time… If not, don’t worry. you helped me enough alraedy
s
Glad to hear about they “key”. Yeah it’s usually the culprit, if you use it, it must be correct, else everything breaks. I am not familiar enough with the view system to know about this layout_behavior thing, but Scaffold doesn’t do any magic like that afaik, just that it gives you an easy to use slot api to lay out how a lot of common screens are laid out. Plus using subcomposition it can do the calculations for you needed to properly layout snackbars and such, otherwise it can be tricky to do it yourself, when you may/may not have bottom bars, IME showing and stuff like that. Now this last one looks a bit too tricky for me to deduce just from looking at it, and I haven’t quite seen something like it before. Allow me to skip this one 😅 I’m really glad I could help you up to this point at least 😊
488 Views