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

Time:11-11

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?

CodePudding user response:

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

CodePudding user response:

Another way you can use is as follows

Let's do it step by step :)

First Step : the ViewModel must implement the DefaultLifecycleObserver interface

@HiltViewModel
class LoginViewModel @Inject constructor(
    private val savedStateHandle: SavedStateHandle
) : ViewModel(), DefaultLifecycleObserver {
}

in this way you can acess this methods:


public interface DefaultLifecycleObserver extends FullLifecycleObserver {

    /**
     * Notifies that {@code ON_CREATE} event occurred.
     * <p>
     * This method will be called after the {@link LifecycleOwner}'s {@code onCreate}
     * method returns.
     *
     * @param owner the component, whose state was changed
     */
    @Override
    default void onCreate(@NonNull LifecycleOwner owner) {
    }

    /**
     * Notifies that {@code ON_START} event occurred.
     * <p>
     * This method will be called after the {@link LifecycleOwner}'s {@code onStart} method returns.
     *
     * @param owner the component, whose state was changed
     */
    @Override
    default void onStart(@NonNull LifecycleOwner owner) {
    }

    /**
     * Notifies that {@code ON_RESUME} event occurred.
     * <p>
     * This method will be called after the {@link LifecycleOwner}'s {@code onResume}
     * method returns.
     *
     * @param owner the component, whose state was changed
     */
    @Override
    default void onResume(@NonNull LifecycleOwner owner) {
    }

    /**
     * Notifies that {@code ON_PAUSE} event occurred.
     * <p>
     * This method will be called before the {@link LifecycleOwner}'s {@code onPause} method
     * is called.
     *
     * @param owner the component, whose state was changed
     */
    @Override
    default void onPause(@NonNull LifecycleOwner owner) {
    }

    /**
     * Notifies that {@code ON_STOP} event occurred.
     * <p>
     * This method will be called before the {@link LifecycleOwner}'s {@code onStop} method
     * is called.
     *
     * @param owner the component, whose state was changed
     */
    @Override
    default void onStop(@NonNull LifecycleOwner owner) {
    }

    /**
     * Notifies that {@code ON_DESTROY} event occurred.
     * <p>
     * This method will be called before the {@link LifecycleOwner}'s {@code onDestroy} method
     * is called.
     *
     * @param owner the component, whose state was changed
     */
    @Override
    default void onDestroy(@NonNull LifecycleOwner owner) {
    }
}

Second Step: In your Composable, call the method that I have sent to the attachment as below.

create an extension function like this:

@Composable
fun <LO : LifecycleObserver> LO.ObserveLifecycle(lifecycle: Lifecycle) {
  DisposableEffect(lifecycle) {
    lifecycle.addObserver(this@ObserveLifecycle)
    onDispose {
      lifecycle.removeObserver(this@ObserveLifecycle)
    }
  }
}

Call it in your composable as follows

@Composable
fun LoginScreen(viewModel: LoginViewModel) {
    viewModel.observeLifecycle(LocalLifecycleOwner.current.lifecycle)
}  

Thirst Step: Implement your logic in ViewModel and if you are going to go to another page, inform Composable by Event

LoginViewModel.kt
 override fun onResume(owner: LifecycleOwner) {
        super.onResume(owner)
 }
  • Related