Search code examples
androidkotlinandroid-jetpack-compose

Jetpack Compose - How to trigger an event when a screen is composed


My application uses OpenID to authenticate the user.

The first page is more of a splash screen that hands the user to a webpage to authorise if needed or just perform a background refresh of the token the navigate to the main screen.

I'm unsure of how to start the authentication flow without a button click

@Composable
fun LoginScreen(viewModel: LoginViewModel) {
    val ctx = LocalContext.current
    AppTheme {
        Screen()
    }
    viewModel.performLogin(ctx)
}       

Doing the above works, but it then gets called again when the app navigates to the main screen.

fun loginComplete(navController: NavHostController) {
    navController.navigate("main")
}

@Composable
fun MyApp(viewModel: LoginViewModel) {
    val navController = rememberNavController()
    viewModel.setOnLoginCompete(navController, ::loginComplete)
    NavHost(navController, startDestination = "login") {
        composable(route = "login") {
            LoginScreen(viewModel)
        }
        composable(route = "main") {
            MainScreen()
        }
    }
}

I don't think I'm supposed to call the performLogin function like I am in a Composable function, but I can't see another way. What am I missing?


Solution

  • You can tie your flow to lifecycle callbacks. You can create utility composable for handling lifecycle events.

    @Composable
    fun OnLifecycleEvent(onEvent: (owner: LifecycleOwner, event:Lifecycle.Event) -> Unit) {
        val eventHandler = rememberUpdatedState(onEvent)
        val lifecycleOwner = rememberUpdatedState(LocalLifecycleOwner.current)
    
        DisposableEffect(lifecycleOwner.value) {
            val lifecycle = lifecycleOwner.value.lifecycle
            val observer = LifecycleEventObserver { owner, event ->
                eventHandler.value(owner, event)
            }
    
            lifecycle.addObserver(observer)
            onDispose {
                lifecycle.removeObserver(observer)
            }
        }
    }
    

    And the use it like this, if you want to start logging flow when your app comes to the foreground:

    @Composable
    fun MyApp(viewModel: LoginViewModel) {
    
    ...
        OnLifecycleEvent { owner, event ->
            when (event) {
                Lifecycle.Event.ON_RESUME -> { viewModel.performLogin(ctx) }
                else -> { ... }
           }
        }
    }
    

    I actually used this question and its answer as the source