Reputation: 929
I am trying to migrate a project from Material2 specs to Material 3 compose library following this guide.
The current LazyColumn in Material2 has integrate Pull to refresh functionality, found here
However, i did not find any such functionality for Material3. There is an issue tracker open here but they say this functionality will be considered only mid this year, which is too long to wait.
Does anyone have any idea on how this situation should be handled? Pull to refresh is an important flow of our apps user flow, so not having it is not an option.
Should we use accompanist/swipeToRefresh
even though it is deprecated?
I am not proficient in writing custom compose components yet, so any help in figuring this out would be appreciated
Upvotes: 22
Views: 16023
Reputation: 1146
I finally figure out how to do this properly. (Thanks to everyone who mentioned PullToRefreshBox, because I looked at that code to see how it works.)
First and foremost, the obvious. Replace the material dependencies in your build.gradle with the material3 dependencies:
//api(compose.material)
api(compose.material3)
// For desktop - if like me you're using KMP
//implementation("org.jetbrains.compose.material:material-desktop:<version>")
implementation("org.jetbrains.compose.material3:material3-desktop:<version>")
// For Android
//implementation("androidx.compose.material:material:<version>")
implementation("androidx.compose.material3:material3-android:<version>")
Now, on to the real changes. Step 1:
Change the pullRefreshState
to a pullToRefreshState
. This sounds like an easy task, but it also requires you to move some things. In material2 pullRefreshState
contains some things, including isRefreshing
and onRefresh
. These things are now in the modifier in material3. But don't just delete them, you probably want to copy (or cut) them so that you can use them in step 2.
Step 2:
Change your modifier from .pullRefresh(pullRefreshState)
to .pullToRefresh(isRefreshing, pullToRefreshState, onRefresh)
Remember that this requires you to put some information that you removed from step 1 into these values.
Step 3: (This is the part that most people seem to be missing)
Change your PullRefreshIndicator
to PullToRefreshDefaults.Indicator
. These have very similar parameters, but slightly different names.
backgroundColor
is now containerColor
contentColor
is now just color
scale
is goneThe indicator also seems to behave slightly differently. Mostly that in material2 you could pull down to see the indicator and change your mind part way through, pushing the indicator back up, and it wouldn't refresh. That doesn't seem to be possible in material3. You just pull down on your list and eventually the indicator shows that it's refreshing.
Upvotes: 0
Reputation: 483
First add version 1.3.0-beta04
of material3
implementation("androidx.compose.material3:material3:1.3.0-beta04")
Now implement pull to refresh as given below
var isRefreshing by remember { mutableStateOf(false) }
PullToRefreshBox(
isRefreshing = isRefreshing,
onRefresh = {
coroutineScope.launch {
isRefreshing = true
viewModel.refreshData()
isRefreshing = false
}
},
) {
LazyColumn {
items(items) { item->
// Render Items
}
}
}
Upvotes: 9
Reputation: 12300
In 2024, you can use these material3 components:
val pullToRefreshState = rememberPullToRefreshState()
if (pullToRefreshState.isRefreshing) {
LaunchedEffect(true) {
delay(1000)
pullToRefreshState.endRefresh()
}
}
and around your scrolling Column:
Box(Modifier.nestedScroll(pullToRefreshState.nestedScrollConnection)) {
Column(modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())) {
...
}
PullToRefreshContainer(
modifier = Modifier.align(Alignment.TopCenter),
state = pullToRefreshState,
)
}
Upvotes: 7
Reputation: 118
So in the end, I resolved this not by upgrading to the latest beta 1.3.0-beta01 as this introduced other problems, most notably with PullToRefresh that is broken in this build. But instead by simply ensuring that my ModalBottomSheet's content
was explicitly adding sufficient padding:
ModalBottomSheet(
// ... however you want to configure it
) {
Column(
modifier = Modifier.navigationBarsPadding() // This is where the magic happens
) { ... }
}
This handles cases where there is a software navigation bar as well as when there is not (such as when the device is configured to use gestures instead)
Upvotes: -3
Reputation: 14399
In androidx.compose.material3:material3
version 1.3.0-beta01
is available the Composable PullToRefreshBox
which can be used to replace the Box
+ PullRefreshIndicator
from M2:
Scaffold(
topBar = {
TopAppBar(
title = { Text("Title") },
// Provide an accessible alternative to trigger refresh.
actions = {
IconButton(
enabled = !viewModel.isRefreshing,
onClick = { viewModel.refresh() },
) {
Icon(Icons.Filled.Refresh, "Trigger Refresh")
}
},
)
},
) {
PullToRefreshBox(
modifier = Modifier.padding(it),
isRefreshing = viewModel.isRefreshing,
onRefresh = { viewModel.refresh() },
) {
LazyColumn(Modifier.fillMaxSize()) {
if (!viewModel.isRefreshing) {
items(viewModel.itemCount) {
ListItem({ Text(text = "Item ${viewModel.itemCount - it}") })
}
}
}
}
}
More samples are available here
Upvotes: 1
Reputation: 1256
It looks like currently there's a dedicated Material3 component for this: PullToRefreshContainer.
See docs at https://developer.android.com/reference/kotlin/androidx/compose/material3/pulltorefresh/package-summary
At the time of writing the api is experimental and I'd expect it to be updated to make it easier to hoist the state out of the UI element, but the example in the docs provides a starting point for migrating. It lacks a callback on refresh starting, which is something you'd have to work around in the interim.
Upvotes: 4
Reputation: 256
Our team ran into this same problem. Ultimately, we opted to bring the MD2 implementation of the PullToRefresh component into our project as a temporary workaround. Then, we just updated any MD2 references to their MD3 counterparts.
The full MD2 implementation consists of the following files:
This approach was suggested in this discussion. You can find the op's full solution here.
Upvotes: 24