In modern Android app development, user authentication is a crucial feature. Firebase provides a robust and easy-to-use solution for implementing various authentication methods. Jetpack Compose, Android’s modern UI toolkit, offers a declarative way to build user interfaces. This combination allows developers to create secure and visually appealing authentication flows with less boilerplate.
What is Firebase Authentication?
Firebase Authentication provides backend services, easy-to-use SDKs, and UI libraries to authenticate users to your app. It supports authentication using passwords, phone numbers, popular federated identity providers like Google, Facebook, and Twitter, and more.
Why Use Firebase Authentication with Jetpack Compose?
- Simplified Authentication: Firebase simplifies the backend complexity of user authentication.
- Declarative UI: Jetpack Compose allows you to build reactive and visually appealing UI components for the authentication process.
- Cross-Platform Support: Firebase supports Android, iOS, and web, enabling you to share authentication logic across platforms.
How to Implement User Authentication with Firebase and Jetpack Compose
Here’s how to implement user authentication using Firebase in a Jetpack Compose application:
Step 1: Set Up a Firebase Project
- Go to the Firebase Console.
- Click on \”Add project\” and follow the instructions to create a new project.
- In your project overview, click the Android icon to add Firebase to your Android app.
- Register your app by providing the package name, and optionally a SHA-1 certificate.
- Download the
google-services.json
file and add it to yourapp/
directory.
Step 2: Add Firebase Dependencies
In your project’s build.gradle
file, add the Google Services plugin:
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'com.google.gms.google-services' // Add this line
}
In your app’s build.gradle
file, add the Firebase Authentication dependency and the Kotlin coroutines library:
dependencies {
implementation 'com.google.firebase:firebase-auth-ktx:22.3.1' // Use the latest version
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3' // For asynchronous tasks
}
Make sure to sync your Gradle files after adding the dependencies.
Step 3: Initialize Firebase
Initialize Firebase in your application’s Application
class. If you don’t have one, create it:
import android.app.Application
import com.google.firebase.FirebaseApp
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
}
}
Then, register this class in your AndroidManifest.xml
:
<application
android:name=\".MyApplication\"
...>
<!-- Other attributes -->
</application>
Step 4: Implement Authentication UI with Jetpack Compose
Create composable functions for sign-up, sign-in, and sign-out screens. Here’s an example of a simple sign-up screen:
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
@Composable
fun SignUpScreen(onSignUpSuccess: () -> Unit, onSignUpFailed: (String?) -> Unit) {
var email by remember { mutableStateOf(\"\") }
var password by remember { mutableStateOf(\"\") }
val coroutineScope = rememberCoroutineScope()
val authService = remember { FirebaseAuthService() }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
OutlinedTextField(
value = email,
onValueChange = { email = it },
label = { Text(\"Email\") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField(
value = password,
onValueChange = { password = it },
label = { Text(\"Password\") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
Button(
onClick = {
coroutineScope.launch {
try {
authService.signUp(email, password)
onSignUpSuccess()
} catch (e: Exception) {
onSignUpFailed(e.message)
}
}
},
modifier = Modifier.fillMaxWidth()
) {
Text(\"Sign Up\")
}
}
}
@Preview(showBackground = true)
@Composable
fun SignUpScreenPreview() {
SignUpScreen(onSignUpSuccess = {}, onSignUpFailed = {})
}
Step 5: Create a Firebase Authentication Service
Implement a service class to handle the Firebase authentication logic:
import com.google.firebase.auth.FirebaseAuth
import kotlinx.coroutines.tasks.await
class FirebaseAuthService {
private val auth: FirebaseAuth = FirebaseAuth.getInstance()
suspend fun signUp(email: String, password: String) {
auth.createUserWithEmailAndPassword(email, password).await()
}
suspend fun signIn(email: String, password: String) {
auth.signInWithEmailAndPassword(email, password).await()
}
fun signOut() {
auth.signOut()
}
fun getCurrentUser() = auth.currentUser
}
Step 6: Handle Sign-in and Sign-out
Implement composables for the sign-in screen and the sign-out functionality:
import androidx.compose.runtime.*
@Composable
fun SignInScreen(onSignInSuccess: () -> Unit, onSignInFailed: (String?) -> Unit) {
var email by remember { mutableStateOf(\"\") }
var password by remember { mutableStateOf(\"\") }
val coroutineScope = rememberCoroutineScope()
val authService = remember { FirebaseAuthService() }
// UI similar to SignUpScreen
// In Button onClick:
coroutineScope.launch {
try {
authService.signIn(email, password)
onSignInSuccess()
} catch (e: Exception) {
onSignInFailed(e.message)
}
}
}
@Composable
fun SignOutButton(onSignOut: () -> Unit) {
val coroutineScope = rememberCoroutineScope()
val authService = remember { FirebaseAuthService() }
Button(
onClick = {
coroutineScope.launch {
authService.signOut()
onSignOut()
}
}
) {
Text(\"Sign Out\")
}
}
Step 7: Observe Authentication State
Create a composable that observes the authentication state and navigates between the sign-in/sign-up screen and the main content:
import androidx.compose.runtime.*
import androidx.navigation.compose.rememberNavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
@Composable
fun AuthNavigation() {
val navController = rememberNavController()
val authService = remember { FirebaseAuthService() }
val currentUser = remember { mutableStateOf(authService.getCurrentUser()) }
NavHost(navController = navController, startDestination = if (currentUser.value != null) \"main\" else \"auth\") {
composable(\"auth\") {
AuthScreen(
onAuthSuccess = {
currentUser.value = authService.getCurrentUser()
navController.navigate(\"main\") {
popUpTo(\"auth\") { inclusive = true }
}
}
)
}
composable(\"main\") {
MainScreen(
onSignOut = {
authService.signOut()
currentUser.value = null
navController.navigate(\"auth\") {
popUpTo(\"main\") { inclusive = true }
}
}
)
}
}
}
@Composable
fun AuthScreen(onAuthSuccess: () -> Unit) {
// Display SignUpScreen or SignInScreen based on a toggle
}
@Composable
fun MainScreen(onSignOut: () -> Unit) {
// Display the main content of your app
}
Conclusion
Combining Firebase Authentication with Jetpack Compose allows you to create a modern, secure, and visually appealing authentication system in your Android apps. By leveraging Firebase’s backend services and Compose’s declarative UI, you can focus on creating a great user experience while ensuring secure user management.