Hi Im currently struggling with navigation in Jetpack Compose
due to @composable invocations can only happen from the context of an @composable function
. I have a function:
private fun signInResult(result: FirebaseAuthUIAuthenticationResult) {
val response = result.idpResponse
if (result.resultCode == RESULT_OK) {
user = FirebaseAuth.getInstance().currentUser
Log.e("MainActivity.kt", "Innlogging vellykket")
ScreenMain()
} else {
Log.e("MainActivity.kt", "Feil med innlogging" response?.error?.errorCode)
}
}
and used with my navigation class shown under I only get the error message shown above, how do I fix it?
@Composable
fun ScreenMain(){
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Routes.Vareliste.route) {
composable(Routes.SignUp.route) {
SignUp(navController = navController)
}
composable(Routes.ForgotPassword.route) { navBackStack ->
ForgotPassword(navController = navController)
}
composable(Routes.Vareliste.route) { navBackStack ->
Vareliste(navController = navController)
}
composable(Routes.Handlekurv.route) { navBackStack ->
Handlekurv(navController = navController)
}
composable(Routes.Profileromoss.route) { navBackStack ->
Profileromoss(navController = navController)
}
}
}
EDIT WITH COMPLETE CODE Here is the whole code for the class if you guys wanted to see it!
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
LoginPage()
}
}
}
}
private var user: FirebaseUser? = FirebaseAuth.getInstance().currentUser
private lateinit var auth: FirebaseAuth
@Composable
fun LoginPage() {
Box(modifier = Modifier.fillMaxSize()) {
}
Column(
modifier = Modifier.padding(20.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Velkommen til ITGuys", style = TextStyle(fontSize = 36.sp))
Spacer(modifier = Modifier.height(20.dp))
Box(modifier = Modifier.padding(40.dp, 0.dp, 40.dp, 0.dp)) {
Button(
onClick = { signIn() },
shape = RoundedCornerShape(50.dp),
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
) {
Text(text = "Logg inn")
}
}
}
}
private fun signIn() {
val providers = arrayListOf(
AuthUI.IdpConfig.EmailBuilder().build(),
AuthUI.IdpConfig.GoogleBuilder().build()
)
val signinIntent = AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.build()
signInLauncher.launch(signinIntent)
}
private val signInLauncher = registerForActivityResult(
FirebaseAuthUIActivityResultContract()
) {
res -> this.signInResult(res)
}
private fun signInResult(result: FirebaseAuthUIAuthenticationResult) {
val response = result.idpResponse
if (result.resultCode == RESULT_OK) {
user = FirebaseAuth.getInstance().currentUser
Log.e("MainActivity.kt", "Innlogging vellykket")
ScreenMain()
} else {
Log.e("MainActivity.kt", "Feil med innlogging" response?.error?.errorCode)
}
}
}
I need to add more text to be allowed to post this much code you can ignore this text cause it is just for being able to post.
CodePudding user response:
As @z.y mentioned, you can pass a lambda with a onFirebaseAuthSuccess. I would also add that as you are passing the navController to the signup screen, the lambda callback you need to pass should look something like
onFirebaseAuthSuccess = { navController.navigate(Routes.Profileromoss.route) } - or whatever route you need
Based on
composable(Routes.SignUp.route) {
SignUp(navController = navController)
}
I would assume your signIn
screen is called from inside the scope of a composable. If you can add the extract of code containing how you are calling the signInResult
function we can be sure about it.
CodePudding user response:
I'm not familiar with Firebase Authentication
so I'm not sure where do you call or how you use your signInResult
function but you cannot invoke a function that is annotated with @Composable
(ScreenMain
) from a scope that is not annotated by it such as ordinary
function (signInResult
).
You can consider adding a lambda
callback for signInResult
which will be called in the RESULT_OK
condition block.
private fun signInResult(result: FirebaseAuthUIAuthenticationResult, onFirebaseAuthSuccess: () -> Unit) {
val response = result.idpResponse
if (result.resultCode == RESULT_OK) {
...
...
onFirebaseAuthSuccess() // this callback
} else {
...
}
}
Edit: @sgtpotatoe has better answer, you can invoke a navigation
in your root composable from the lambda callback
that will navigate to your target screen.
CodePudding user response:
Ok so, in your MainActivity, you want your navigational component to be at the top:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
ScreenMain()
}
}
}
}
Then add a route to your navhost for the login page:
composable(Routes.LoginPage.route) {
LoginPage(navController = navController)
}
I think its a bit of a major change, but you would have to rely on a view model to make the authentication, so it can handle the calls, not blocking the ui or showing a loading screen, and communicate with the view
It would look something like this:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel = MyViewModel()
setContent {
JetpackComposeDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
ScreenMain(viewModel)
}
}
}
}
In the LoginPage you want to access the viewmodel to start the auth service calls
In the LoginPage you want to access the viewmodel to observe if the call is succesfull, and in that case do the navigation
In the MyViewModel you want to have the authentication calls, and to update the variable that triggers the navigation if auth is succesfull
Here is an example of a sample firebase authentication app in compose, I would use it as a guide https://firebase.blog/posts/2022/05/adding-firebase-auth-to-jetpack-compose-app