I am using Jetpack Compose and Room as database solution. Have a room table with Task
s and retrieve all tasks using a flow to display them in a lazy list, like so:
@Entity(tableName="task")
data class Task(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
val text: String,
val done: Boolean,
)
@Dao
interface TaskDao {
// ...other methods
@Query("SELECT * FROM task") fun getAllTasks(): Flow<List<Task>>
@Query("UPDATE todo SET done = :done WHERE id = :id")
suspend fun setDone(id: Int, done: Boolean)
}
class TaskRepository(private val taskDao: TaskDao) {
// ...other methods
fun getAllTasks() = taskDao.getAllTasks()
suspend fun setDone(id: Int, done: Boolean) = taskDao.setDone(id, done)
}
I provide the list of tasks via my ViewModel:
class MainScreenViewModel(
private val taskRepository: DatabaseTaskRepository,
) : ViewModel() {
val taskList: StateFlow<List<Task>> =
taskRepository
.getAllTasks()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = emptyList()
)
fun setDone(taskId: Int, done: Boolean) {
viewModelScope.launch { taskRepository.setDone(taskId, done) }
}
In my MainScreen I now display a list of the tasks:
@Composable
fun MainScreen(viewModel: MainScreenViewModel) {
val tasks by viewModel.taskList.collectAsState()
TaskList(
tasks = tasks,
onDoneChanged = viewModel::setDone,
)
}
@Composable
fun TaskList(
tasks: List<Task>,
onDoneChanged: (id: Int, done: Boolean) -> Unit,
) {
LazyColumn {
items(items = tasks, key = { it.id }) {
TaskListItem(
text = it.text,
done = it.done,
onDoneChanged = { done -> onDoneChanged(it.id, done) },
)
}
}
}
@Composable
fun TaskListItem(
text: String,
done: Boolean,
onDoneChanged: (Boolean) -> Unit,
) {
Row(/* ... */) {
Checkbox(checked = done, onCheckedChange = onDoneChanged)
Text(text)
}
}
This works, but every time I check a task, all items get recomposed. I suppose this is because the taskList flow emits a new List and compose doesn't know that actually only one item changed. But how can I make this clear to compose and achieve that only the checkbox gets recomposed? I thought providing LazyColumn
with a key
function would solve the issue as this is implied in the compose best practices, but it didn't. I'm sure there must be a way? I know using a SnapshotStateList
works from this post, but as I need to get (and update) the data from the DB I guess this isn't an option?