Search code examples
androidandroid-jetpack-composepull-to-refresh

PullRefreshIndicator does not disappear after Refreshing


I have migrated from Accompanist SwipeRefresh (deprecated) to PullRefreshIndicator which is now the default indicator for Compose.

The first issue I encountered was PullRefreshIndicator overlapping other components, but I managed this by using zIndex as explained on this SO question.

The problem now is that the indicator is not disappearing/hidden after a refresh as illustrated below.

enter image description here

I have narrowed the issue down to the state type passed to rememberPullRefreshState().

val state by viewModel.fixturesState.collectAsState()
val pullRefreshState = rememberPullRefreshState(
        refreshing = state.isLoading/isRefreshing,
        onRefresh = { viewModel.onEvent(HomeEvent.OnSwipeRefresh) }
    )

In my ViewModel I have two states i.e. isLoading and isRefreshing . State isLoading is triggered when the data is loading while State isRefreshing is activated as an Event when the user pulls down to refresh. The isLoading state is toggled between false and true by flow emission in relation to the data fetch. The isRefreshing state is switched to true following a refresh action by the user and is switched back to false from inside the method that is fetching the data.

This is the link to HomeScreen.

If I pass isLoading the indicator is correctly hidden after the refresh.

 val pullRefreshState = rememberPullRefreshState(
        refreshing = state.isLoading,
        onRefresh = { viewModel.onEvent(HomeEvent.OnSwipeRefresh) }
    )

However, if I pass isRefreshing the indicator doesn't go away after the refresh.

val pullRefreshState = rememberPullRefreshState(
        refreshing = state.isRefreshing,
        onRefresh = { viewModel.onEvent(HomeEvent.OnSwipeRefresh) }
    )

I could could leave the code this way (stick with isLoading) and move on but I notice some inconsistencies because everytime there is loading, the indicator briefly appears and then goes away which is not desirable for UI/UX.

Anybody with any leads to rectify this issue please help. I am using Material 3 [1.1.0-alpha04] and Compose Version 1.3


Solution

  • This issue is fixed in compose 1.4.0-alpha03, according to https://issuetracker.google.com/issues/248274004. A delay of 1 second is really long. It is possible to fix it without delay:

    @Composable
    @OptIn(ExperimentalMaterialApi::class)
    fun PullRefreshBox(
        modifier: Modifier = Modifier,
        refreshing: Boolean,
        onRefresh: () -> Unit = {},
        content: @Composable () -> Unit,
    ) {
        // The LaunchedEffect is needed to fix the hiding of the indicator: https://issuetracker.google.com/issues/248274004
        // Without this workaround the indicator would be visible even if the loading finished
        var isRefreshingWorkaround by remember { mutableStateOf(refreshing) }
        LaunchedEffect(key1 = refreshing) {
            isRefreshingWorkaround = refreshing
        }
        val state = rememberPullRefreshState(isRefreshingWorkaround, onRefresh)
        Box(
            modifier = modifier.pullRefresh(state),
        ) {
            content()
            PullRefreshIndicator(
                modifier = Modifier.align(Alignment.TopCenter),
                refreshing = isRefreshingWorkaround,
                state = state,
                contentColor = MaterialTheme.colorScheme.primary,
            )
        }
    }