
Facilitates efficient networking and database caching with a fault-tolerant architecture, offering seamless state management, offline data access, and non-blocking operations for a smooth app experience.
Flower is a Kotlin multi-platform (originally, Android) library that makes networking and database caching easy. It enables developers to fetch network resources and use them as is OR combine them with local database at single place with fault-tolerant architecture.
Loading, Success, EmptySuccess, Error) of
resources efficiently.main thread when accessing
network/database resources.Flower is recognised by Google Dev Library, a showcase of open-source projects.
Flower is primarily available in two modules, one for Ktorfit and the other for Retrofit.
If you want to handle networking yourself, you can also use the core module.
$flowerVersion=3.0.0
$ktorFitVersion=1.0.0-beta12
$retrofitVersion=2.9.0
This is a multiplatform module. It is suitable for use in Kotlin multiplatform projects, Android (Apps/Libraries), the JVM in general, Kotlin-JS, and so on...
It uses and provides Ktorfit and you must use KSP in your project.
Apply the KSP Plugin to your project:
plugins {
id("com.google.devtools.ksp") version "1.7.10-1.0.6"
}Multiplatform example
dependencies {
implementation("io.github.hadiyarajesh.flower-ktorfit:flower-ktorfit:$flowerVersion")
add("kspCommonMainMetadata", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
add("kspJvm", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
add("kspAndroid", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
add("kspIosX64", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
add("kspJs", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
add("kspIosSimulatorArm64", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
}Android example
dependencies {
implementation("io.github.hadiyarajesh.flower-ktorfit:flower-ktorfit:$flowerVersion")
//Ktorfit library
implementation("de.jensklingenberg.ktorfit:ktorfit-lib:$ktorFitVersion")
ksp("de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
}This is an Android-only module, so it can only be used in Android Apps/Libs.
dependencies {
implementation("io.github.hadiyarajesh.flower-retrofit:flower-retrofit:$flowerVersion")
// Retrofit library
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
}This module only contains the core code and allows you to handle the networking yourself.
We Highly recommend you to use either Ktorfit or Retrofit module. Only use this if you don't want to rely on Ktorfit or Retrofit.
dependencies {
implementation("io.github.hadiyarajesh:flower-core:$flowerVersion")
}Assume you have a model class called MyModel that you are retrieving from the network.
data class MyModel(
val id: Long,
val data: String
)Flow<MyModel>.Android Room example:
@Dao
interface MyDao {
@Query("SELECT * FROM Data")
fun getLocalData(): Flow<MyModel>
}ApiResponse<MyModel> (
or Flow<ApiResponse<MyModel>> if you're retrieving a flow of data from server)Ktorfit/Retrofit example:
interface MyApi {
@GET("data")
suspend fun getRemoteData(): ApiResponse<MyModel>
// OR
@GET("data")
fun getRemoteData(): Flow<ApiResponse<MyModel>>
}Ktorfit
Add FlowerResponseConverter as ResponseConverter in Ktorfit builder.
Ktorfit.Builder()
.baseUrl(BASE_URL)
.httpClient(ktorClient)
.responseConverter(FlowerResponseConverter())
.build()Retrofit
Add FlowerCallAdapterFactory as CallAdapterFactory in Retrofit builder
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addCallAdapterFactory(FlowerCallAdapterFactory.create())
.build()2.1 If you want to fetch network resources and save into local database,
use dbBoundResource() higher order function
(or dbBoundResourceFlow() function if you're retrieving a flow of data from server).
It takes following functions as parameters.
MyModel) to local databasefun getMyData(): Flow<Resource<MyModel>> {
return dbBoundResources(
fetchFromLocal = { myDao.getLocalData() },
shouldMakeNetworkRequest = { localData -> localData == null },
makeNetworkRequest = { myApi.getRemoteData() },
processNetworkResponse = { },
saveResponseData = { myDao.saveMyData(it) },
onNetworkRequestFailed { errorMessage, statusCode -> }
).flowOn(Dispatchers.IO)
}OR
2.2 If you only want to fetch network resources without saving it in local database,
use networkResource() higher order function.
(or networkResourceFlow() function if you're retrieving a flow of data from server)
fun getMyData(): Flow<Resource<MyModel>> {
return networkResource(
makeNetworkRequest = { myApi.getRemoteData() },
onNetworkRequestFailed { errorMessage, statusCode -> }
).flowOn(Dispatchers.IO)
}// A model class to re-present UI state
sealed class UiState<out T> {
object Empty : UiState<Nothing>()
data class Loading(val data: T?) : UiState<out T>()
data class Success<out T>(val data: T & Any) : UiState<T & Any>()
data class Error(val msg: String?) : UiState<Nothing>()
}private val _myData: MutableStateFlow<UiState<MyModel>> = MutableStateFlow(UiState.Empty)
val myData: StateFlow<UiState<MyModel>> = _myData.asStateFlow()
init {
viewModelScope.launch {
getMyData()
}
}
suspend fun getMyData() = repository.getMyData().collect { response ->
when (response.status) {
is Resource.Status.Loading -> {
val status = response.status as Resource.Status.Loading
_myData.value = UiState.Loading(status.data)
}
is Resource.Status.Success -> {
val status = response.status as Resource.Status.Success
_myData.value = UiState.Success(status.data)
}
// EmptySuccess is for potentially body-less successful HTTP responses like 201, 204
is Resource.Status.EmptySuccess -> {
_myData.value = UiState.Empty
}
is Resource.Status.Error -> {
val status = response.status as Resource.Status.Error
_myData.value = UiState.Error(status.message)
}
}
}lifecycleScope.launchWhenStarted {
viewModel.myData.collect { data ->
when (data) {
is UiState.Loading -> {
// Show loading
}
is UiState.Success -> {
// Show success
}
is UiState.Error -> {
// Show error
}
else -> {}
}
}
}Two sample apps are provided in this repository
Flower is a Kotlin multi-platform (originally, Android) library that makes networking and database caching easy. It enables developers to fetch network resources and use them as is OR combine them with local database at single place with fault-tolerant architecture.
Loading, Success, EmptySuccess, Error) of
resources efficiently.main thread when accessing
network/database resources.Flower is recognised by Google Dev Library, a showcase of open-source projects.
Flower is primarily available in two modules, one for Ktorfit and the other for Retrofit.
If you want to handle networking yourself, you can also use the core module.
$flowerVersion=3.0.0
$ktorFitVersion=1.0.0-beta12
$retrofitVersion=2.9.0
This is a multiplatform module. It is suitable for use in Kotlin multiplatform projects, Android (Apps/Libraries), the JVM in general, Kotlin-JS, and so on...
It uses and provides Ktorfit and you must use KSP in your project.
Apply the KSP Plugin to your project:
plugins {
id("com.google.devtools.ksp") version "1.7.10-1.0.6"
}Multiplatform example
dependencies {
implementation("io.github.hadiyarajesh.flower-ktorfit:flower-ktorfit:$flowerVersion")
add("kspCommonMainMetadata", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
add("kspJvm", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
add("kspAndroid", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
add("kspIosX64", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
add("kspJs", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
add("kspIosSimulatorArm64", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
}Android example
dependencies {
implementation("io.github.hadiyarajesh.flower-ktorfit:flower-ktorfit:$flowerVersion")
//Ktorfit library
implementation("de.jensklingenberg.ktorfit:ktorfit-lib:$ktorFitVersion")
ksp("de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorFitVersion")
}This is an Android-only module, so it can only be used in Android Apps/Libs.
dependencies {
implementation("io.github.hadiyarajesh.flower-retrofit:flower-retrofit:$flowerVersion")
// Retrofit library
implementation("com.squareup.retrofit2:retrofit:$retrofitVersion")
}This module only contains the core code and allows you to handle the networking yourself.
We Highly recommend you to use either Ktorfit or Retrofit module. Only use this if you don't want to rely on Ktorfit or Retrofit.
dependencies {
implementation("io.github.hadiyarajesh:flower-core:$flowerVersion")
}Assume you have a model class called MyModel that you are retrieving from the network.
data class MyModel(
val id: Long,
val data: String
)Flow<MyModel>.Android Room example:
@Dao
interface MyDao {
@Query("SELECT * FROM Data")
fun getLocalData(): Flow<MyModel>
}ApiResponse<MyModel> (
or Flow<ApiResponse<MyModel>> if you're retrieving a flow of data from server)Ktorfit/Retrofit example:
interface MyApi {
@GET("data")
suspend fun getRemoteData(): ApiResponse<MyModel>
// OR
@GET("data")
fun getRemoteData(): Flow<ApiResponse<MyModel>>
}Ktorfit
Add FlowerResponseConverter as ResponseConverter in Ktorfit builder.
Ktorfit.Builder()
.baseUrl(BASE_URL)
.httpClient(ktorClient)
.responseConverter(FlowerResponseConverter())
.build()Retrofit
Add FlowerCallAdapterFactory as CallAdapterFactory in Retrofit builder
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addCallAdapterFactory(FlowerCallAdapterFactory.create())
.build()2.1 If you want to fetch network resources and save into local database,
use dbBoundResource() higher order function
(or dbBoundResourceFlow() function if you're retrieving a flow of data from server).
It takes following functions as parameters.
MyModel) to local databasefun getMyData(): Flow<Resource<MyModel>> {
return dbBoundResources(
fetchFromLocal = { myDao.getLocalData() },
shouldMakeNetworkRequest = { localData -> localData == null },
makeNetworkRequest = { myApi.getRemoteData() },
processNetworkResponse = { },
saveResponseData = { myDao.saveMyData(it) },
onNetworkRequestFailed { errorMessage, statusCode -> }
).flowOn(Dispatchers.IO)
}OR
2.2 If you only want to fetch network resources without saving it in local database,
use networkResource() higher order function.
(or networkResourceFlow() function if you're retrieving a flow of data from server)
fun getMyData(): Flow<Resource<MyModel>> {
return networkResource(
makeNetworkRequest = { myApi.getRemoteData() },
onNetworkRequestFailed { errorMessage, statusCode -> }
).flowOn(Dispatchers.IO)
}// A model class to re-present UI state
sealed class UiState<out T> {
object Empty : UiState<Nothing>()
data class Loading(val data: T?) : UiState<out T>()
data class Success<out T>(val data: T & Any) : UiState<T & Any>()
data class Error(val msg: String?) : UiState<Nothing>()
}private val _myData: MutableStateFlow<UiState<MyModel>> = MutableStateFlow(UiState.Empty)
val myData: StateFlow<UiState<MyModel>> = _myData.asStateFlow()
init {
viewModelScope.launch {
getMyData()
}
}
suspend fun getMyData() = repository.getMyData().collect { response ->
when (response.status) {
is Resource.Status.Loading -> {
val status = response.status as Resource.Status.Loading
_myData.value = UiState.Loading(status.data)
}
is Resource.Status.Success -> {
val status = response.status as Resource.Status.Success
_myData.value = UiState.Success(status.data)
}
// EmptySuccess is for potentially body-less successful HTTP responses like 201, 204
is Resource.Status.EmptySuccess -> {
_myData.value = UiState.Empty
}
is Resource.Status.Error -> {
val status = response.status as Resource.Status.Error
_myData.value = UiState.Error(status.message)
}
}
}lifecycleScope.launchWhenStarted {
viewModel.myData.collect { data ->
when (data) {
is UiState.Loading -> {
// Show loading
}
is UiState.Success -> {
// Show success
}
is UiState.Error -> {
// Show error
}
else -> {}
}
}
}Two sample apps are provided in this repository