Step #2: Passing the Event Up the Chain

Eventually, we need to modify our repository to reflect the change in the model state.

We could have the RosterRowHolder do that. However, it is best to minimize the number of places that you are modifying your data. The more your model-manipulating code is scattered, the more difficult it will be to change that code, such as when we want to start storing this stuff in a database. Since we already are working with the repository in RosterMotor, we may as well have it handle the model modifications as well.

However, our RosterRowHolder does not have access to the RosterMotor. We could pass it down from the RosterListFragment if we wanted. Alternatively, we can pass the event up the Kotlin object hierarchy, from the RosterRowHolder through the RosterAdapter to the RosterListFragment, and from there affect our RosterMotor.

With that in mind, modify the constructor of RosterRowHolder to look like:

class RosterRowHolder(
  private val binding: TodoRowBinding,
  val onCheckboxToggle: (ToDoModel) -> Unit
) :

Here, onCheckboxToggle is a function type. We are passing some sort of function or lambda expression into RosterRowHolder that takes a ToDoModel as input and returns nothing (i.e., Unit, roughly analogous to void in Java).

Then, revise bind() on RosterRowHolder to look like:

  fun bind(model: ToDoModel) {
    binding.apply {
      isCompleted.isChecked = model.isCompleted
      isCompleted.setOnCheckedChangeListener { _, _ -> onCheckboxToggle(model) }
      desc.text = model.description
    }
  }

Now, our setOnCheckedChangeListener() calls the onCheckboxToggle function type, passing in the current model. This replaces the TODO() that we used in the previous step.

Now, though, RosterAdapter will have a compile error, as we are not passing in this value. So, add a similar constructor parameter to RosterAdapter:

class RosterAdapter(
  private val inflater: LayoutInflater,
  private val onCheckboxToggle: (ToDoModel) -> Unit
) :

Then, pass onCheckboxToggle to the RosterRowHolder constructor call in onCreateViewHolder():

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
    RosterRowHolder(
      TodoRowBinding.inflate(inflater, parent, false),
      onCheckboxToggle
    )

So now we are passing the onCheckboxToggle that the RosterAdapter receives to each of the RosterRowHolder instances. This, though, has broken RosterListFragment, as it is not passing a value for onCheckboxToggle in its RosterAdapter constructor call.

To fix this, modify the creation of the RosterAdapter instance in RosterListFragment to look like:

    val adapter = RosterAdapter(layoutInflater) { model ->
      TODO()
    }

When a function type is the last parameter for a function call, we can use a lambda expression outside of the function call parentheses. So, the lambda expression that we have here turns into onCheckboxToggle.

Right now, we have a TODO() function call in the lambda expression. So, if you run the app and you click on one of the CheckBox widgets… you crash with a error of:

kotlin.NotImplementedError: An operation is not implemented.

However, this is an expected crash. NotImplementedError is what TODO() throws. So, this confirms that we got control in RosterListFragment when the user clicked the CheckBox. Now, we need to replace the TODO() with something more useful and less crash-prone.


Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.