Search code examples
androidandroid-jetpack-composedagger-hiltandroid-jetpack-navigation

NullPointerException on navigate function - Jetpack Compose NavHostController


I implemented Google OneTap Authentication using FirebaseAuth on my Jetpack Compose application. The problem I face is that after I sign in successfully, if I close my application and start it again I get a NullPointerException caused by my checkAuthState() function which redirects the user to the profile page if it is already authenticated.

Error:

FATAL EXCEPTION: main
                                                                                                    Process: com.codrutursache.casey, PID: 28428
                                                                                                    java.lang.NullPointerException
                                                                                                        at androidx.navigation.NavController.navigate(NavController.kt:1652)
                                                                                                        at androidx.navigation.NavController.navigate(NavController.kt:1984)
                                                                                                        at androidx.navigation.NavController.navigate$default(NavController.kt:1979)
                                                                                                        at com.codrutursache.casey.presentation.MainActivity.navigateToProfileScreen(MainActivity.kt:78)
                                                                                                        at com.codrutursache.casey.presentation.MainActivity.checkAuthState(MainActivity.kt:74)
                                                                                                        at com.codrutursache.casey.presentation.MainActivity.access$checkAuthState(MainActivity.kt:33)
                                                                                                        at com.codrutursache.casey.presentation.MainActivity$onCreate$1.invoke(MainActivity.kt:68)
                                                                                                        at com.codrutursache.casey.presentation.MainActivity$onCreate$1.invoke(MainActivity.kt:42)
                                                                                                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                                                                                                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                                                                                        at androidx.compose.ui.platform.ComposeView.Content(ComposeView.android.kt:428)
                                                                                                        at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:252)
                                                                                                        at androidx.compose.ui.platform.AbstractComposeView$ensureCompositionCreated$1.invoke(ComposeView.android.kt:251)
                                                                                                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                                                                                                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                                                                                        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                                                                                                        at androidx.compose.ui.platform.CompositionLocalsKt.ProvideCommonCompositionLocals(CompositionLocals.kt:195)
                                                                                                        at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:119)
                                                                                                        at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt$ProvideAndroidCompositionLocals$3.invoke(AndroidCompositionLocals.android.kt:118)
                                                                                                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                                                                                                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                                                                                        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                                                                                                        at androidx.compose.ui.platform.AndroidCompositionLocals_androidKt.ProvideAndroidCompositionLocals(AndroidCompositionLocals.android.kt:110)
                                                                                                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Wrapper.android.kt:158)
                                                                                                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1$2.invoke(Wrapper.android.kt:157)
                                                                                                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                                                                                                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                                                                                        at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228)
                                                                                                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:157)
                                                                                                        at androidx.compose.ui.platform.WrappedComposition$setContent$1$1.invoke(Wrapper.android.kt:142)
                                                                                                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:108)
                                                                                                        at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                                                                                        at androidx.compose.runtime.ActualJvm_jvmKt.invokeComposable(ActualJvm.jvm.kt:78)
                                                                                                        at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3340)
                                                                                                        at androidx.compose.runtime.ComposerImpl.composeContent$runtime_release(Composer.kt:3273)
                                                                                                        at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:588)
                                                                                                        at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:1013)
                                                                                                        at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:520)
                                                                                                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:142)
                                                                                                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:133)
2024-01-08 18:13:08.263 28428-28428 AndroidRuntime          com.codrutursache.casey              E      at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:1191)
                                                                                                        at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:133)
                                                                                                        at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:183)
                                                                                                        at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.kt:314)
                                                                                                        at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.kt:192)
                                                                                                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:140)
                                                                                                        at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:133)
                                                                                                        at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:1266)
                                                                                                        at android.view.View.dispatchAttachedToWindow(View.java:21980)
                                                                                                        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3490)
                                                                                                        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3497)
                                                                                                        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3497)
                                                                                                        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3497)
                                                                                                        at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:3497)
                                                                                                        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3014)
                                                                                                        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2465)
                                                                                                        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9305)
                                                                                                        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1339)
                                                                                                        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1348)
                                                                                                        at android.view.Choreographer.doCallbacks(Choreographer.java:952)
                                                                                                        at android.view.Choreographer.doFrame(Choreographer.java:882)
                                                                                                        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1322)
                                                                                                        at android.os.Handler.handleCallback(Handler.java:958)
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                        at android.os.Looper.loopOnce(Looper.java:205)
                                                                                                        at android.os.Looper.loop(Looper.java:294)
                                                                                                        at android.app.ActivityThread.main(ActivityThread.java:8177)
                                                                                                        at java.lang.reflect.Method.invoke(Native Method)
                                                                                                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

This is my MainActivity code which handles the auth check and instantiates the navController:

@AndroidEntryPoint
@ExperimentalAnimationApi
class MainActivity : ComponentActivity() {

    private lateinit var navController: NavHostController
    private lateinit var authViewModel: AuthViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            navController = rememberNavController()
            authViewModel = hiltViewModel<AuthViewModel>()

            val isBottomBarVisible =
                rememberSaveable { mutableStateOf(false) }

            CaseyTheme {
                isBottomBarVisible.value = when (currentRoute(navController)) {
                    Route.AuthRoute.route -> false
                    Route.ProfileRoute.route -> true
                    else -> true
                }
                Scaffold(
                    bottomBar = {
                        if (isBottomBarVisible.value)
                            BottomBar(navController = navController)
                    }
                ) { paddingValues ->
                    NavGraph(
                        navController = navController,
                        modifier = Modifier.padding(paddingValues)
                    )
                }
            }

            checkAuthState()
        }
    }

    private fun checkAuthState() {
        if (authViewModel.isUserAuthenticated) {
            navigateToProfileScreen()
        }
    }

    private fun navigateToProfileScreen() = navController.navigate("profile_screen")

}

@Composable
fun currentRoute(navController: NavController): String? {
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    return navBackStackEntry?.destination?.route
}

Solution

  • I can't exactly tell where the error is coming from, as you are using a fairly atypical structure in your Activity. You can try to move the checkAuthState function call into a LaunchedEffect like this:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CaseyTheme {
    
                navController = rememberNavController()
                authViewModel = hiltViewModel<AuthViewModel>()
                val isBottomBarVisible = rememberSaveable { mutableStateOf(false) }
    
                isBottomBarVisible.value = when (currentRoute(navController)) {
                    Route.AuthRoute.route -> false
                    Route.ProfileRoute.route -> true
                    else -> true
                }
    
                Scaffold(
                    bottomBar = {
                        if (isBottomBarVisible.value)
                            BottomBar(navController = navController)
                    }
                ) { paddingValues ->
    
                    LaunchedEffect(Unit) {
                        checkAuthState()
                    }
    
                    NavGraph(
                        navController = navController,
                        modifier = Modifier.padding(paddingValues)
                    )
                }
            }
        }
    }
    

    A typical Activity in Compose looks like this:

    class MainActivity : ComponentActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContent {
                CaseyTheme {
                    Surface(
                        modifier = Modifier.fillMaxSize(),
                        color = MaterialTheme.colorScheme.background
                    ) {
                        CaseyApp()
                    }
                }
            }
        }
    }
    

    Then, you place your root Scaffold into the CaseyApp Composable:

    @Composable
    CaseyApp() {
        navController = rememberNavController()
        //...
    
         Scaffold(
             //...
          ) { paddingValues ->
    
              LaunchedEffect(Unit) {
                  checkAuthState()
              }
    
             //...
         }
    }