Jetpack Compose Material3 Pull to Refresh functionality
U

6

20

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

Undercurrent answered 9/3, 2023 at 10:14 Comment(0)
R
24

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:

  • PullRefresh.kt
  • PullRefreshIndicator.kt
  • PullRefreshIndicatorTransform.kt
  • PullRefreshState.kt

This approach was suggested in this discussion. You can find the op's full solution here.

Richmal answered 10/3, 2023 at 15:48 Comment(1)
I just don't understand why to make it so difficult to install such library, I ended up simply copy and pasting those files. People like to make things harder for absolutely zero-to-none benefits, time is money. And you just made me save a lot, thanks.Arianna
B
5

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,
      )
  }
Bea answered 18/6 at 12:38 Comment(0)
S
4

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.

Shererd answered 5/3 at 14:23 Comment(0)
P
1

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

Persuade answered 22/5 at 13:50 Comment(3)
Unfortunately it doesn't actually do anything.Cepeda
@Cepeda probably you need to fix your code: the snipped I posted is taken directly from the official AndroidX samples and it works fine for me when I use it.Persuade
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: 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)Cepeda
P
0

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
        }
    }
}
Priggish answered 24/7 at 10:50 Comment(0)
C
-1

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)

Cepeda answered 5/6 at 10:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.