androidandroid-jetpack-compose

LaunchedEffect(Unit) doesn't trigger if app is reopened


If my app has never refreshed some user details before upon launch, I want it to do so.

I tried putting the call to refresh in a LaunchedEffect(Unit). Now let's say the request fails. I can have a snackbar with a retry button, but what about when the user presses the back button and the app is reopened? No recomp seems to occur to automatically try again, so is the only option in this case to tie the logic to the lifecycle of the app, something like this answer? It seems very counterintuitive to Compose, and a very common use case to refresh data when an app is resumed from the background.

    when (uiState) {
        HomeUiState.RequiresInitializing -> {
            LaunchedEffect(Unit) {
                refresh()
            }
            Box(
                modifier = modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) {
                LoadingWheel()
            }
        }
    }


Solution

  • If I understand what you are trying to achieve correctly, once an app/screen is "resumed" you want to refresh some data?

    You can create something like this :

    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.LaunchedEffect
    import androidx.compose.ui.platform.LocalLifecycleOwner
    import androidx.lifecycle.Lifecycle
    import androidx.lifecycle.Lifecycle.State.RESUMED
    import androidx.lifecycle.repeatOnLifecycle
    import kotlinx.coroutines.CoroutineScope
    
    
    @Composable
    fun RepeatOnLifecycleEffect(
        state: Lifecycle.State = RESUMED,
        action: suspend CoroutineScope.() -> Unit
    ) {
        val lifecycle = LocalLifecycleOwner.current
    
        LaunchedEffect(key1 = Unit) {
            lifecycle.repeatOnLifecycle(state, block = action)
        }
    }
    

    This will perform an "action" each time the lifecycle is resumed, or choose another lifecycle state ...

    For your use case maybe :

    @Composable
    fun HomeScreen(
        uiState : HomeUiState,
        modifier: Modifier = Modifier
    ) {
        ....
        
        RepeatOnLifecycleEffect(action = { refresh() })
    }
    

    This means you do not need HomeUiState.RequiresInitializing.