I am currently migrating an android application from XML, Databinding and Flow/LiveData to Compose, Flow/StateFlow.
In this application, I have a very simple Room database:
@Entity
data class User(@PrimaryKey var id: Int = 0, var name: String)
@Dao
interface UserDAO
{
@Query("Select * from User where id = :userId")
fun getUserFlow(userId: Int): Flow<User?>
@Transaction
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun createNewUser(user: User)
@Transaction
@Update(onConflict = OnConflictStrategy.IGNORE)
suspend fun updateUser(user: User)
}
@Database(
entities = [User::class],
version = 1,
exportSchema = true
)
abstract class UserDatabase
: RoomDatabase()
{
abstract fun userDao(): UserDAO
}
From a ViewModel, I create, update and collect the data from the database:
class MainViewModel(application: Application)
: AndroidViewModel(application)
{
private val userDatabase by lazy { Room.databaseBuilder(application, UserDatabase::class.java, "user-data.db").build() }
private val user = userDatabase.userDao().getUserFlow(0).stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), null)
val name = user.map {
it?.name ?: "no name"
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), "pouet")
fun create()
{
viewModelScope.launch(Dispatchers.IO) {
userDatabase.userDao().createNewUser(User(name = "Test"))
}
}
fun update()
{
viewModelScope.launch(Dispatchers.IO) {
user.value?.name = UUID.randomUUID().toString()
user.value?.let {
userDatabase.userDao().updateUser(it)
}
}
}
}
And then I collect the StateFlow using collectAsStateWithLifecycle into the activity:
class MainActivity : ComponentActivity()
{
private val model: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
val name = model.name.collectAsStateWithLifecycle()
Column(modifier = Modifier.fillMaxSize()) {
Button(
onClick = { model.create() }) {
Text("Create")
}
Button(
onClick = { model.update() }) {
Text("Update")
}
Text(
text = "Hello ${name.value}!",
)
}
}
}
}
}
When I click on the create button, the user is inserted in the database but the UI is not updated.
When I click on the update button, the user name is updated correctly in the database but the UI is not updated.
Using Android Studio, if I change directly the user name in the database, the UI is correctly updated.
I do not understand why the StateFlow is correctly collected when I update manually the database using Android Studio but it does not work when the database is updated using the code.
Thank you for your help!