How do people design pull to refresh with Material...
# compose
m
How do people design pull to refresh with Material 3, where the top app bar is not elevated? There is nowhere for the pull to to refresh indicator to come from, normally it's pulled from "under" the top app bar, but that doesn't work visually on Material 3's flat top app bars.
s
Make it come off of the top of the screen. And adjust the height in which it starts refreshing so that it doesn't start refreshing too far up. That's what I do in some screens where the top app bar exists, but it doesn't have a different color from the rest of the screen. Does it feel too awkward when the color is different but there isn't a feeling of elevation? I haven't tried that before but I can imagine how it'd be the case.
m
Thanks, pulling it from the top of the screen seems to work fine, although it looks a bit different to what I’m used to.
c
Instagram has a nice example of this where the spinner incrementally builds in as you pull and then does the refresh. Something like that or fading in the spinner as you pull. So instead of looking like it’s pulled out from under on the Y axis, you can pull from “behind” along the Z axis, if that makes sense.
The Now in Android sample I believe has a similar loading pull spinner also
s
I really think this looks fine though, what do you think? And the only thing I’ve had to do to make it refresh at the same Y position as you’d expect (so add the system bar height) is this
Copy code
val systemBarInsetTopDp = with(LocalDensity.current) {
  WindowInsets.systemBars.getTop(this).toDp()
}
val pullRefreshState = rememberPullRefreshState(
  refreshing = isLoading,
  onRefresh = reload,
  refreshingOffset = PullRefreshDefaults.RefreshingOffset + systemBarInsetTopDp,
)
// And then just call the composable inside a Box() which is full-screen, aligned to TopCenter
PullRefreshIndicator(
  refreshing = isLoading,
  state = pullRefreshState,
  scale = true,
  modifier = Modifier.align(Alignment.TopCenter),
)
c
It’s alright but might better if you could offset it to start appearing further down the screen versus all the way from the top. Not sure if the component is that flexible though as I haven’t tried it myself lately
s
When pulled further down from the top, the existing
androidx.compose.material.pullrefresh.PullRefreshIndicator
composable behaves like this, it looks cut-off which imo looks even worse no?
c
Can fade in as you pull be added?
s
Uhm don’t think so. When I add just
Copy code
.graphicsLayer {
  alpha = pullRefreshState.progress
  // clip = true // If I add this or not add it, I get in both cases super odd behavior, only with clip = true it doesn't show up at all when actually loading as the video shows
},
I get this super odd behavior blob shrug
l
Yeah, this is because the indicator is already inside a graphics layer that is translated - so you can’t really apply an alpha layer to this. Hard to say what makes the most sense here, but maybe it would work if you offset the indicator by the status bar height, so it starts drawing from the statusbar? E.g
Modifier.offset(y = -systemBarInsetTopDp)
Oh I see, you had this before. The design of the indicator is really meant for it to appear from ‘underneath’ something like an app bar, so without that coming from the top of the screen is probably the only thing that makes sense I guess
s
Yeah that's kinda what I am doing in the video above, which imo looks not that good, but then when I make it start from the top Chris thinks it doesn't look so good 😅 I'm personally not 100% sure what the best idea is overall
l
There’s no design guidance for this sort of use case, so for something custom like an indicator that fades in place you would probably need to build your own for that
m
There’s no design guidance
I wonder if that's just not done yet, or is it lacking on purpose and pull to refresh is just not part of the Material 3 design system. Especially that the Material3 Compose library has removed it.
l
Pull refresh for Material 3 is being worked on, I meant more for M2 / older, it was never part of the older design guidance
Actually, I take that back, there is some platform guidance but it wasn’t fully implemented in SwipeRefreshLayout, I think it’s just a UX mock https://m2.material.io/design/platform-guidance/android-swipe-to-refresh.html#behavior-placement
c
Yeah I think my designer mind is thinking of all the pull to refresh scenarios in iOS where the spinner shows up in the space that is made available by the pull, which works as the iOS pattern for overscrolling is to create space, while in modern Android overscroll is now just stretching the content.
My personal take if we were to rewrite the guidance in 2023 is that pull to refresh spinner should be able to positionally start right above the content being pulled and fade in, rather than needing to appear from behind a top bar, depending on the design
Sidenote: I just went and looked at all the pull to refresh usages in some of the Google apps. They all appear from behind a top bar, but some are actually pretty janky when slowed down, like the Google Docs one 🙈
s
Uuuuhhh 😅
Play Console goes all the way to the top like what I show in the original message btw.
360 Views