Step #7: Adapting EditFragment
Fixing EditFragment
is more involved.
Partly, that is because we use more functions from SingleModelMotor
, such as in the fragment’s save()
and delete()
functions. Change those to get the ToDoModel
by getting the value
from the StateFlow
:
private fun save() {
binding?.apply {
val model = motor.states.value.item
val edited = model?.copy(
description = desc.text.toString(),
isCompleted = isCompleted.isChecked,
notes = notes.text.toString()
) ?: ToDoModel(
description = desc.text.toString(),
isCompleted = isCompleted.isChecked,
notes = notes.text.toString()
)
edited.let { motor.save(it) }
}
navToDisplay()
}
private fun delete() {
val model = motor.states.value.item
model?.let { motor.delete(it) }
navToList()
}
value
on StateFlow
is whatever the last-emitted object was. In our case, it is the last-emitted view-state. So, here, we get the last-emitted view-state, get the model from it (if there was a model), and proceed as we did before.
In terms of populating the widgets, you might think that we would use the same approach that we did in DisplayFragment
: collect()
the StateFlow
and use the model (if we have one) for the widget contents. Indeed, we do that… but there is a problem.
We are only updating our model when the user clicks the save app bar item. In particular, the user can edit the description or notes, or check the is-completed CheckBox
, and then undergo a configuration change. Those edits are not reflected in our model, because we have not yet updated it — the user did not click the save app bar item. However, we really should try to hold onto the user’s edits, as the user may get irritated if we lose them just because they rotated the screen.
The good news is that Android automatically knows how to handle those edits. That savedInstanceState
Bundle
that we see in functions like onViewCreated()
contains the edits, put there by Android as part of processing the configuration change. Even better is that Android automatically updates the widgets with those edits in the new fragment after the configuration change.
We just need to not screw it up.
Specifically, we need to make sure that if we have a saved instance state, it gets used to populate our widgets. Getting the data from the model is to be used if we do not have the state.
So, with all that in mind, replace onViewCreated()
in EditFragment
with this implementation:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
motor.states.collect { state ->
if (savedInstanceState == null) {
state.item?.let {
binding?.apply {
isCompleted.isChecked = it.isCompleted
desc.setText(it.description)
notes.setText(it.notes)
}
}
}
}
}
}
We once again collect()
our StateFlow
, and we once again use the view-state to update the widgets… but only if our savedInstanceState
is null
. Otherwise, we assume that our widgets already have what we want from a pre-configuration change instance of our fragment.
At this point, the app should compile and run. More importantly, courtesy of the changes that we made in the past few tutorials, any to-do items that you enter will be saved and will be available in future runs of the app.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.