This document summarizes how to build an Android app using Jetpack Compose and Firebase. It covers an overview of Compose and Firebase, authentication using Firebase Authentication, calling asynchronous Firebase methods, and using Cloud Firestore for data storage. The app architecture is presented using MVVM and services are introduced to encapsulate Firebase functionality. Code examples are provided for authentication, listening to Firestore document changes, and updating the UI based on async operations.
10. Jetpack Compose
● Android’s modern toolkit for building native UI
● Intuitive and requires less code than writing .xml files
● First stable version of Compose was launched in 2021
11. Jetpack Compose
@Composable
fun Hello() {
Column(modifier = Modifier.padding(16.dp)) {
var name by remember { mutableStateOf(“”) }
if (name.isNotEmpty()) {
Text(text = "Hello, $name!")
}
}
}
16. How it used to be
private lateinit var auth: FirebaseAuth
public override fun onCreate() {
super.onCreate()
auth = Firebase.auth
}
public override fun onStart() {
super.onStart()
val currentUser = auth.currentUser
}
17. How it is now
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { FirstComposableFunction() }
}
}
18. How it is now
class AccountServiceImpl() : AccountService {
override fun getUser(): FirebaseUser? {
return Firebase.auth.currentUser
}
}
20. Synchronous and Asynchronous methods
● Synchronous
○ Returns directly to the caller
○ Blocks the caller thread
● Asynchronous
○ Processing in another thread
○ Return to caller thread once it’s completed
21. How callback works
fun authenticate(
email: String,
password: String,
onResult: (Throwable?) -> Unit
) {
Firebase.auth.signInWithEmailAndPassword(email,password)
.addOnCompleteListener { onResult(it.exception) }
}
23. Authentication
● Knowing a user's identity allows your app to do many things
○ Securely save user data in the cloud
○ Provide the same personalized experience across all devices
● Firebase Authentication provides backend services, SDKs and UI libraries
● It supports different authentication methods
● Offers Anonymous Authentication
28. Adding to code
Add to app/build.gradle file:
dependencies {
implementation 'com.google.firebase:firebase-auth-ktx'
}
Sync Android project with Gradle files again
36. Cloud Firestore
● NoSQL document database in the Cloud
● Data is stored into structures called documents
● Retrieve individual or list of documents
● Listen to changes across client apps
● Offers offline support
38. Mapping data
data class Task(
val id: String = “”,
val title: String = "",
val priority: String = "",
val dueDate: String = "",
val dueTime: String = "",
val description: String = "",
val url: String = "",
val flag: Boolean = false,
val completed: Boolean = false,
val userId: String = ""
)
val task = result.toObject<Task>()
40. Adding to code
Add to app/build.gradle file:
dependencies {
implementation 'com.google.firebase:firebase-firestore-ktx'
}
Sync Android project with Gradle files again
41. The service
val query = Firebase.firestore
.collection(TASK_COLLECTION)
.whereEqualTo(USER_ID, userId)
query.addSnapshotListener { value, _ ->
value?.documentChanges?.forEach {
val wasDocumentDeleted = it.type == REMOVED
onDocumentEvent(
wasDocumentDeleted, it.document.toObject<Task>()
)
}
}
42. The ViewModel
private fun onDocumentEvent(wasDocumentDeleted: Boolean, task: Task) {
if (wasDocumentDeleted) tasks.remove(task)
else updateTaskInList(task)
}
private fun updateTaskInList(task: Task) {
val index = tasks.indexOfFirst { it.id == task.id }
if (index < 0) tasks.add(task) else tasks[index] = task
}
var tasks = mutableStateListOf<Task>()
private set
44. There's a lot more in Firestore!
fun addListener(
userId: String,
onDocumentEvent: (Boolean, Task) -> Unit,
onError: (Throwable) -> Unit
)
fun removeListener()
fun getTask(taskId: String, onError: (Throwable) -> Unit, onSuccess: (Task) -> Unit)
fun saveTask(task: Task, onResult: (Throwable?) -> Unit)
fun deleteTask(taskId: String, onResult: (Throwable?) -> Unit)
fun deleteAllForUser(userId: String, onResult: (Throwable?) -> Unit)