Jetpack Compose is a modern toolkit for building native Android user interfaces. Firebase Authentication provides backend services for easy and secure user sign-in. Combining these tools allows developers to create seamless authentication flows within their Compose apps. This guide covers the essential steps to integrate Firebase Authentication into your Jetpack Compose project.

Setting Up Firebase in Your Android Project

Start by creating a Firebase project in the Firebase Console. Link your Android app by registering it with your project, then download the google-services.json file and add it to your app module directory. Ensure your project-level build.gradle includes the Google services classpath and apply the plugin in your app-level build.gradle.

In your app's build.gradle, add the Firebase SDK dependencies:

implementation 'com.google.firebase:firebase-auth-ktx'

Sync your project to download dependencies.

Implementing Firebase Authentication

Create a singleton or a class to manage FirebaseAuth instance:

val auth: FirebaseAuth = FirebaseAuth.getInstance()

Sign-In Function

Implement functions to handle user sign-in, sign-up, and sign-out:

fun signIn(email: String, password: String, onResult: (Boolean, String?) -> Unit) {
    auth.signInWithEmailAndPassword(email, password)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                onResult(true, null)
            } else {
                onResult(false, task.exception?.message)
            }
        }
}

Creating the Compose UI

Design your login screen with Compose components, including text fields and buttons:

@Composable
fun LoginScreen(
    onLoginSuccess: () -> Unit,
    viewModel: AuthViewModel = viewModel()
) {
    val email = remember { mutableStateOf("") }
    val password = remember { mutableStateOf("") }
    val context = LocalContext.current

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center
    ) {
        TextField(
            value = email.value,
            onValueChange = { email.value = it },
            label = { Text("Email") },
            modifier = Modifier.fillMaxWidth()
        )
        Spacer(modifier = Modifier.height(8.dp))
        TextField(
            value = password.value,
            onValueChange = { password.value = it },
            label = { Text("Password") },
            visualTransformation = PasswordVisualTransformation(),
            modifier = Modifier.fillMaxWidth()
        )
        Spacer(modifier = Modifier.height(16.dp))
        Button(
            onClick = {
                viewModel.signIn(email.value, password.value) { success, message ->
                    if (success) {
                        onLoginSuccess()
                    } else {
                        Toast.makeText(context, message ?: "Login failed", Toast.LENGTH_SHORT).show()
                    }
                }
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Login")
        }
    }
}

Connecting ViewModel with Firebase

Create a ViewModel to handle authentication logic and expose state to the UI:

class AuthViewModel : ViewModel() {
    private val auth = FirebaseAuth.getInstance()

    fun signIn(email: String, password: String, onResult: (Boolean, String?) -> Unit) {
        auth.signInWithEmailAndPassword(email, password)
            .addOnCompleteListener { task ->
                if (task.isSuccessful) {
                    onResult(true, null)
                } else {
                    onResult(false, task.exception?.message)
                }
            }
    }
}

Handling Authentication State

Observe the current user to update UI accordingly:

@Composable
fun MainScreen() {
    val auth = FirebaseAuth.getInstance()
    val user = auth.currentUser

    if (user != null) {
        // User is signed in
        WelcomeScreen()
    } else {
        // User is not signed in
        LoginScreen(onLoginSuccess = { /* navigate to main content */ })
    }
}

Conclusion

Integrating Firebase Authentication with Jetpack Compose involves setting up Firebase, implementing authentication logic, and designing reactive UI components. This combination enables secure and seamless user login experiences in modern Android applications. Remember to handle user state properly and provide feedback to ensure a smooth user experience.