Jetpack Compose load Firestore Data with Flow
November 2, 2021Create a class to handle data state (loading, error, success), as I believe it is impossible to handle exception within composable.
enum class Status {
SUCCESS,
ERROR,
LOADING
}
// https://github.com/android/architecture-components-samples/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/vo/Resource.kt
data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(msg: String, data: T?): Resource<T> {
return Resource(Status.ERROR, data, msg)
}
fun <T> loading(data: T?): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
Data Class
data class Post(var id: String, var created: Timestamp? = null, var title: String? = null) {
companion object {
fun toObject(doc: DocumentSnapshot): Post? {
val item = doc.toObject<Post>()
item?.id = doc.id
return item
}
}
}
ViewModel to load data from Firestore using Flow
class MainViewModel : ViewModel() {
// consider using Dependency injection with Hilt
private val auth: FirebaseAuth = Firebase.auth
private val db = Firebase.firestore
fun fetchPosts() = callbackFlow {
val collection = db.collection("posts")
val snapshotListener = collection.orderBy("created", Query.Direction.DESCENDING).addSnapshotListener { value, error ->
val response = if (error == null && value != null) {
val data = value.documents.map { doc ->
Post.toObject(doc)
}
Resource.success(data)
} else {
Timber.e(error)
Resource.error(error.toString(), null)
}
offer(response)
}
awaitClose() {
snapshotListener.remove()
}
}
}
Composable
@Composable
fun JourneyApp(viewModel: MainViewModel = viewModel()) {
val postsResource by viewModel.fetchPosts().collectAsState(initial = Resource.loading(null))
val posts = postsResource.data ?: emptyList()
if (postsResource.status == Status.ERROR) {
Text("Error: ${postsResource.message}")
}
else if (postsResource.status == Status.LOADING) {
Text("Loading ....")
}
else {
LazyColumn {
items(posts) { post ->
Text(post.title)
}
}
}
}
- algo-trading
- algolia
- analytics
- android
- android-ktx
- android-permission
- android-studio
- apps-script
- bash
- binance
- bootstrap
- bootstrapvue
- chartjs
- chrome
- cloud-functions
- coding-interview
- contentresolver
- coroutines
- crashlytics
- crypto
- css
- dagger2
- datastore
- datetime
- docker
- eslint
- firebase
- firebase-auth
- firebase-hosting
- firestore
- firestore-security-rules
- flask
- fontawesome
- fresco
- git
- github
- glide
- godot
- google-app-engine
- google-cloud-storage
- google-colab
- google-drive
- google-maps
- google-places
- google-play
- google-sheets
- gradle
- html
- hugo
- inkscape
- java
- java-time
- javascript
- jetpack-compose
- jetson-nano
- kotlin
- kotlin-serialization
- layout
- lets-encrypt
- lifecycle
- linux
- logging
- lubuntu
- markdown
- mate
- material-design
- matplotlib
- md5
- mongodb
- moshi
- mplfinance
- mysql
- navigation
- nginx
- nodejs
- npm
- nuxtjs
- nvm
- pandas
- payment
- pip
- pwa
- pyenv
- python
- recylerview
- regex
- room
- rxjava
- scoped-storage
- selenium
- social-media
- ssh
- ssl
- static-site-generator
- static-website-hosting
- sublime-text
- ubuntu
- unit-test
- uwsgi
- viewmodel
- viewpager2
- virtualbox
- vue-chartjs
- vue-cli
- vue-router
- vuejs
- vuelidate
- vuepress
- web-development
- web-hosting
- webpack
- windows
- workmanager
- wsl
- yarn