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)
}