Simplify Authentication: A Guide to Streamlining Google Sign-In in Jetpack Compose UI for Effortless User Authentication

Table of Contents

In today’s mobile app development landscape, implementing a smooth and secure authentication process is essential for user engagement and retention. One popular authentication method is Google Sign-In, which allows users to sign in to your app using their Google credentials. In this blog, we will explore how to integrate Google Sign-In seamlessly into your Jetpack Compose UI for Android projects. By following the steps outlined below, you’ll be able to enhance the user experience and streamline the authentication process.

Prerequisites

Before diving into the implementation, ensure that you have a basic understanding of Jetpack Compose and Android development. Familiarity with Kotlin is also beneficial. Additionally, make sure you have set up a project in the Google Cloud Platform (GCP) and obtained the necessary credentials and permissions.

Step 1: Google Sign-In API Adding the Dependency

The first step is to add the required dependency to your project’s build.gradle file. By including the ‘play-services-auth’ library, you gain access to the Google Sign-In API. Make sure to sync the project after adding the dependency to ensure it is correctly imported.

Kotlin
implementation 'com.google.android.gms:play-services-auth:19.2.0'

The version number, in this case, is 19.2.0, which specifies the specific version of the play-services-auth library you want to include.

Step 2: Creating the GoogleUserModel

To handle the user data obtained from the Google Sign-In process, we need to create a data class called ‘GoogleUserModel’. This class will store the relevant user information, such as their name and email address. By encapsulating this data in a model class, we can easily pass it between different components of our app.

Kotlin
data class GoogleUserModel(val name: String?, val email: String?)

Step 3: Implementing the AuthScreen

The ‘AuthScreen’ composable function serves as the entry point for our authentication flow. It interacts with the ‘GoogleSignInViewModel’ and handles the UI components required for the sign-in process. We will create a smooth navigation flow that allows users to initiate the Google Sign-In procedure.

Kotlin
@Composable
fun AuthScreen(
    navController: NavController,
) {
    val signInRequestCode = 1
    // val context = LocalContext.current
    /*val mGoogleSignInViewModel: GoogleSignInViewModel = viewModel(
    factory = GoogleSignInViewModelFactory(context.applicationContext as Application)
    )*/
    val mGoogleSignInViewModel: GoogleSignInViewModel = viewModel()
    val userState = mGoogleSignInViewModel.googleUser.collectAsState()
    val user = userState.value
    val authResultLauncher =
        rememberLauncherForActivityResult(contract = GoogleApiContract()) { task ->
            try {
                val gsa = task?.getResult(ApiException::class.java)
                if (gsa != null) {
                    mGoogleSignInViewModel.fetchSignInUser(gsa.email, gsa.displayName)
                } else {
                    mGoogleSignInViewModel.isError(true)
                }
            } catch (e: ApiException) {
                Log.e("Error in AuthScreen%s", e.toString())
            }
        }
    AuthView(onClick = { authResultLauncher.launch(signInRequestCode) }, mGoogleSignInViewModel)
    // Strange issue after upgrading to latest version
    if (mGoogleSignInViewModel.googleUser.collectAsState().value.name != "") {
        LaunchedEffect(key1 = Unit) {
            mGoogleSignInViewModel.hideLoading()
            // GoogleUserModel(user.name, user.email)
            val userJson =
                MoshiUtils.getJsonAdapter(GoogleUserModel::class.java).lenient().toJson(user)
            navController.navigate(Screen.Settings.passGoogleUserData(userJson)) {
                popUpTo(route = Screen.Auth.route) { inclusive = true }
            }
        }
    }
}

Step 4: Designing the AuthView

In the ‘AuthView’ composable function, we will define the visual layout of our authentication screen. This includes displaying a loading indicator, the Google Sign-In button, and handling potential error messages. By providing a user-friendly interface, we enhance the overall user experience.

Kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun AuthView(onClick: () -> Unit, mGoogleSignInViewModel: GoogleSignInViewModel) {
    Scaffold {
        it.calculateTopPadding()
        if (
            mGoogleSignInViewModel.loading.collectAsState().value == true &&
                !mGoogleSignInViewModel.loading.collectAsState().value
        ) {
            FullScreenLoaderComponent()
        } else {
            Column(
                modifier = Modifier.fillMaxSize().padding(24.dp),
                horizontalAlignment = Alignment.CenterHorizontally,
            ) {
                Spacer(modifier = Modifier.weight(1F))
                Image(
                    painterResource(id = R.mipmap.ic_launcher),
                    contentDescription = stringResource(R.string.app_desc),
                )
                Spacer(modifier = Modifier.weight(1F))
                SignInGoogleButton(
                    onClick = {
                        mGoogleSignInViewModel.showLoading()
                        onClick()
                    }
                )
                Spacer(modifier = Modifier.weight(1F))
                Text(
                    text = APP_SLOGAN,
                    textAlign = TextAlign.Center,
                )
                when {
                    mGoogleSignInViewModel.loading.collectAsState().value -> {
                        /*isError.let {*/
                        Text(AUTH_ERROR_MSG, style = MaterialTheme.typography.bodyLarge)
                        mGoogleSignInViewModel.hideLoading()
                        // }
                    }
                }
            }
        }
    }
}
@Preview(name = "Day Mode", uiMode = Configuration.UI_MODE_NIGHT_NO)
@Composable
fun PreviewAuthView() {
    ResumeSenderTheme { AuthScreen(navController = rememberNavController()) }
}

Code Breakdown

  • The AuthScreen function is a Composable function that represents the authentication screen. It takes a NavController as a parameter, which will be used for navigating to different screens.
  • Inside the AuthScreen function, an instance of GoogleSignInViewModel is created using the viewModel function. This ViewModel is responsible for managing the authentication state related to Google Sign-In.
  • The userState variable collects the state of the googleUser property from the GoogleSignInViewModel as a Composable state. This allows the UI to update reactively whenever the user state changes.
  • The AuthView composable function is called to display the UI components of the authentication screen. It takes a lambda function onClick and the mGoogleSignInViewModel as parameters.
  • After calling the AuthView composable, there is a check to see if the user’s name is not empty. If it’s not empty, a LaunchedEffect is used to perform an action. It hides the loading state, converts the user object to JSON using MoshiUtils, and navigates to the settings screen, passing the user data along.
  • The AuthView composable function is responsible for rendering the UI of the authentication screen. It uses the Scaffold composable to set up the basic layout structure.
  • Inside the AuthView composable, there’s a Column that contains various UI components of the authentication screen, such as an Image, a sign-in button (SignInGoogleButton), and a Text displaying the app’s slogan.
  • The when expression is used to handle different states. In this case, when the loading state of the mGoogleSignInViewModel is true, it displays an error message (AUTH_ERROR_MSG) using the Text composable.
  • Finally, there are @Preview annotations for previewing the AuthView composable in different UI modes (day mode and night mode).

Step 5: Managing Authentication with GoogleSignInViewModel

The ‘GoogleSignInViewModel’ class plays a crucial role in managing the authentication state and communicating with the Google Sign-In API. Depending on your preference, you can choose to use LiveData or StateFlow to update the user’s sign-in status and handle loading and error states. This ViewModel acts as a bridge between the UI and the underlying authentication logic.

Kotlin
/*
 * It contains commented code I think it will helpful when implement logout functionality
 * in future thats why kept as it is here.
 * */
class GoogleSignInViewModel : ViewModel() {
    private var _userState = MutableStateFlow(GoogleUserModel("", ""))
    val googleUser = _userState.asStateFlow()
    private var _loadingState = MutableStateFlow(false)
    val loading = _loadingState.asStateFlow()
    private val _errorStateFlow = MutableStateFlow(false)
    val errorStateFlow = _errorStateFlow.asStateFlow()
    /* init {
    checkSignedInUser(application.applicationContext)
    }*/
    fun fetchSignInUser(email: String?, name: String?) {
        _loadingState.value = true
        viewModelScope.launch {
            _userState.value =
                GoogleUserModel(
                    email = email,
                    name = name,
                )
        }
        _loadingState.value = false
    }
    /* private fun checkSignedInUser(applicationContext: Context) {
     _loadingState.value = true
    val gsa = GoogleSignIn.getLastSignedInAccount(applicationContext)
     if (gsa != null) {
     _userState.value = GoogleUserModel(
     email = gsa.email,
     name = gsa.displayName,
     )
     }
    _loadingState.value = false
     }*/
    fun hideLoading() {
        _loadingState.value = false
    }

    fun showLoading() {
        _loadingState.value = true
    }

    fun isError(isError: Boolean) {
        _errorStateFlow.value = isError
    }
}
/*class GoogleSignInViewModelFactory(
 private val application: Application
) : ViewModelProvider.Factory {
 override fun <T : ViewModel> create(modelClass: Class<T>): T {
 @Suppress("UNCHECKED_CAST")
 if (modelClass.isAssignableFrom(GoogleSignInViewModel::class.java)) {
 return GoogleSignInViewModel(application) as T
 }
 throw IllegalArgumentException("Unknown ViewModel class")
 }
}*/

Conclusion

By following this tutorial, you have learned how to seamlessly integrate Google Sign-In into your Jetpack Compose UI for Android. The integration allows users to sign in to your app using their Google credentials, enhancing the user experience and streamlining the authentication process. By leveraging the power of Jetpack Compose and the Google Sign-In API, you can build secure and user-friendly apps that cater to the modern authentication needs of your users.

Skill Up: Software & AI Updates!

Receive our latest insights and updates directly to your inbox

Related Posts

error: Content is protected !!