androidandroid-jetpack-composenavigation-compose

How to use navigation in Jetpack Compose android


While trying to navigate from MapsScreen to SignupScreen in Jetpack Compose, although I see in logcat that SignUp is clicked,I am not able to navigate to SignupScreen. I have setup the navgraph and the composables. What is missing?

package com.example.bettehomes.presentation.mapscreens

import android.util.Log
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.example.bettehomes.presentation.composables.GeoMarkerTopBar
import com.example.bettehomes.navigation.Screens

@ExperimentalMaterial3Api
@Composable
fun MapsScreen(
    snackbarHostState: SnackbarHostState,
    navController: NavController,
    onNavigateToSignupScreen: () -> Unit,
    fetchLocationUpdates: () -> Unit
) {
    Scaffold(
        topBar = { GeoMarkerTopBar() },
        content = { innerPadding ->
            Box(modifier = Modifier.padding(innerPadding)) {
                MapScreenContent(snackbarHostState, fetchLocationUpdates)
                SnackbarHost(
                    hostState = snackbarHostState,
                    modifier = Modifier
                        .wrapContentHeight(Alignment.Bottom)
                        .align(Alignment.BottomCenter)
                )

                Box(modifier=Modifier.fillMaxSize(),contentAlignment = Alignment.TopCenter) {
                    AnnotatedClickableText(onNavigateToSignupScreen)
                }

            }
        },
        floatingActionButton = {
            ExtendedFloatingActionButton(
                modifier = Modifier
                    .padding(16.dp),
                onClick = {
                    navController.navigate(Screens.GeoMarkerScreen.route)
                },
                icon = {
                    Icon(
                        Icons.Filled.Add,
                        contentDescription = "Create"
                    )
                },
                text = { Text("Mark Area") }
            )
        }
    )
}

@Composable
fun AnnotatedClickableText(onNavigateToSignupScreen:() -> Unit) {
    val annotatedText = buildAnnotatedString {
        //append your initial text
        withStyle(
            style = SpanStyle(
                color = Color.Gray,
            )
        ) {
            append("Don't have an account? ")

        }

        //Start of the pushing annotation which you want to color and make them clickable later
        pushStringAnnotation(
            tag = "SignUp",// provide tag which will then be provided when you click the text
            annotation = "SignUp"
        )
        //add text with your different color/style
        withStyle(
            style = SpanStyle(
                color = Color.Red,
            )
        ) {
            append("Sign Up")
        }
        // when pop is called it means the end of annotation with current tag
        pop()
    }
    ClickableText(
        text = annotatedText,
        onClick = { run { onNavigateToSignupScreen }
            annotatedText.getStringAnnotations(
                tag = "SignUp",// tag which you used in the buildAnnotatedString
                start = it,
                end = it
            )[0].let { annotation ->
                //do your stuff when it gets clicked
                Log.d("Clicked", annotation.item)
            }
        },
    )
}

NavHost.kt

package com.example.bettehomes.navigation

import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.example.bettehomes.data.home.HomeViewModel
import com.example.bettehomes.data.login.LoginViewModel
import com.example.bettehomes.data.signup.SignupViewModel
import com.example.bettehomes.presentation.GeoMarkerViewModel
import com.example.bettehomes.presentation.authscreens.HomeScreen
import com.example.bettehomes.presentation.authscreens.LoginScreen
import com.example.bettehomes.presentation.authscreens.SignUpScreen
import com.example.bettehomes.presentation.authscreens.TermsAndConditionsScreen
import com.example.bettehomes.presentation.mapscreens.GeoMarkerScreen
import com.example.bettehomes.presentation.mapscreens.MapsScreen

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppNavigation(
    navController: NavHostController,
    snackbarHostState: SnackbarHostState,
    homeViewModel: HomeViewModel,
    loginViewModel: LoginViewModel,
    signUpViewModel:SignupViewModel,
    geoMarkerViewModel: GeoMarkerViewModel,
    fetchLocationUpdates: () -> Unit
) {
    NavHost(navController=navController,startDestination = Screens.MapScreen.route) {
        composable(Screens.MapScreen.route) {
            MapsScreen(
                snackbarHostState = snackbarHostState,
                navController = navController,
                onNavigateToSignupScreen = { navController.navigate(Screens.SignUpScreen.route)},
                fetchLocationUpdates)
            }

        composable(Screens.SignUpScreen.route){
            SignUpScreen(signUpViewModel)
        }
        composable(Screens.LoginScreen.route){
            LoginScreen(loginViewModel)
        }
        composable(Screens.TermsAndConditionsScreen.route){
            TermsAndConditionsScreen()
        }
        composable(Screens.HomeScreen.route){
            HomeScreen(homeViewModel)
        }
        composable(Screens.GeoMarkerScreen.route){
            GeoMarkerScreen(geoMarkerViewModel)
        }


    }
}

Solution

  • When you say onNavigateToSignupScreen, you are just referencing a variable - it doesn't actually do anything.

    Instead, if you want to invoke the lambda, you need to use onNavigateToSignupScreen() (which is really just shorthand for what is actually going on - onNavigateToSignupScreen.invoke()).