Advitiay Anand
05/31/2022, 3:31 PMnitrog42
05/31/2022, 3:40 PMLazyColumn {
item {
Header("incomplete")
}
items {}
item {
Header("complete")
}
items {}
}
nitrog42
05/31/2022, 3:42 PMdata class TodoItem(val completed: Boolean, val text: String)
be sure to use val and not var (don't modify your inner data basically) as it might be the issueAdvitiay Anand
05/31/2022, 3:44 PMAdvitiay Anand
06/01/2022, 3:21 PM@Entity(tableName = "todo_items")
data class TodoItem(
@PrimaryKey(autoGenerate = true) val id: Int,
@ColumnInfo var task: String,
@ColumnInfo(name = "is_done") var isDone: Boolean
)
Because of the auto-generated primary keys, I'm simply updating the objects via database calls. The database gets updated fine. But each item's mutable state (inside of the lazy column) is not refreshed as required... causing problems.
Here's the LazyColumn, and the nested Stateful Composable ->
LazyColumn
@Composable
fun TodoItemLists(
incompleteTodos: List<TodoItem>,
completeTodos: List<TodoItem>,
onTaskChange: (TodoItem, String) -> Unit,
onDoneChange: (TodoItem, Boolean) -> Unit,
onDeleteTodo: (TodoItem) -> Unit,
modifier: Modifier = Modifier
) {
LazyColumn(modifier = modifier) {
item {
ListHeading("Incomplete")
}
items(items = incompleteTodos, key = { it.id }) { todoItem ->
DismissibleStateful(onDismiss = { onDeleteTodo(todoItem) }) {
TodoItemRowStateful(
task = todoItem.task,
onTaskChange = { updatedTask ->
onTaskChange(todoItem, updatedTask)
},
isDone = todoItem.isDone,
onDoneChange = { isDoneUpdated ->
onDoneChange(todoItem, isDoneUpdated)
}
)
}
}
item {
ListHeading("Complete")
}
items(items = completeTodos, key = { it.id }) { todoItem ->
DismissibleStateful(onDismiss = { onDeleteTodo(todoItem) }) {
TodoItemRowStateful(
task = todoItem.task,
onTaskChange = { updatedTask ->
onTaskChange(todoItem, updatedTask)
},
isDone = todoItem.isDone,
onDoneChange = { isDoneUpdated ->
onDoneChange(todoItem, isDoneUpdated)
}
)
}
}
}
}
TodoItemRowStateful
@Composable
fun TodoItemRowStateful(
task: String,
onTaskChange: (String) -> Unit,
isDone: Boolean,
onDoneChange: (Boolean) -> Unit
) {
// Duplicated state for the UI to remember
val (taskNameInUi, setTaskNameInUi) = rememberSaveable { mutableStateOf(task) }
val (isTaskDoneInUi, setTaskDoneInUi) = rememberSaveable { mutableStateOf(isDone) }
TodoItemRow(
task = taskNameInUi,
onTaskChange = { taskNameInUiUpdated ->
setTaskNameInUi(taskNameInUiUpdated)
onTaskChange(taskNameInUiUpdated)
},
isDone = isTaskDoneInUi,
onDoneChange = { isTaskDoneInUiUpdated ->
setTaskDoneInUi(isTaskDoneInUiUpdated)
onDoneChange(isTaskDoneInUiUpdated)
}
)
}
Advitiay Anand
06/01/2022, 3:24 PMnitrog42
06/01/2022, 4:16 PMnitrog42
06/01/2022, 4:16 PMAdvitiay Anand
06/01/2022, 4:18 PMnitrog42
06/01/2022, 4:29 PMAdvitiay Anand
06/01/2022, 5:03 PMAdvitiay Anand
06/02/2022, 2:53 PMnitrog42
06/03/2022, 6:56 AM// Duplicated state for the UI to remember
val (taskNameInUi, setTaskNameInUi) = rememberSaveable { mutableStateOf(task) }
val (isTaskDoneInUi, setTaskDoneInUi) = rememberSaveable { mutableStateOf(isDone) }
use directly your todoItem attributes (task/isDone)nitrog42
06/03/2022, 6:57 AMonTaskChange: (TodoItem, String) -> Unit,
onDoneChange: (TodoItem, Boolean) -> Unit,
are doing ?Advitiay Anand
06/03/2022, 7:02 AMAdvitiay Anand
06/03/2022, 7:03 AMnitrog42
06/03/2022, 7:07 AMAdvitiay Anand
06/03/2022, 7:33 AMnitrog42
06/03/2022, 8:29 AM@Entity(tableName = "todo_items")
data class TodoItem(
@PrimaryKey(autoGenerate = true) val id: Int,
@ColumnInfo val task: String,
@ColumnInfo(name = "is_done") val isDone: Boolean
)
use copy in viewModel :
fun updateTodoTask(todoItem: TodoItem, task: String) {
updateTodo(todoItem.copy(task = task))
}
fun updateTodoDone(todoItem: TodoItem, isDone: Boolean) {
updateTodo(todoItem.copy(isDone = isDone))
}
and then to the issue part :
rememberSaveable is probably the root cause of the difference you see in UI.
I would disable it for isDone (you can use directly the database for that), but for the task name, as the keyboard experience is something requiring a good performance, I would avoid updating the database immediatly on each character change, but instead after a delay of typing.
it looks like that :
@Composable
fun TodoItemRowStateful(
task: String,
onTaskChange: (String) -> Unit,
isDone: Boolean,
onDoneChange: (Boolean) -> Unit
) {
// Duplicated state for the UI to remember
val (taskNameInUi, setTaskNameInUi) = rememberSaveable(task) { mutableStateOf(task) }
LaunchedEffect(taskNameInUi) {
delay(300)
onTaskChange(taskNameInUi)
}
TodoItemRow(
task = taskNameInUi,
onTaskChange = { taskNameInUiUpdated ->
setTaskNameInUi(taskNameInUiUpdated)
},
isDone = isDone,
onDoneChange = { isTaskDoneInUiUpdated ->
onDoneChange(isTaskDoneInUiUpdated)
}
)
}
nitrog42
06/03/2022, 8:32 AMval (taskNameInUi, setTaskNameInUi) = remember(task) { mutableStateOf(task) }
Advitiay Anand
06/03/2022, 2:41 PMAdvitiay Anand
06/03/2022, 2:43 PMnitrog42
06/03/2022, 2:44 PMAdvitiay Anand
06/03/2022, 2:49 PM