BenjyTec
BenjyTec

Reputation: 10887

What is the single source of truth in my Fragment with a ViewModel?

I have a Fragment that contains several CheckBoxes:

class TestFragment : Fragment() {

    private lateinit var checkBox2er: CheckBox
    private lateinit var checkBox3er: CheckBox
    // ...
    private lateinit var checkBox10er: CheckBox

    private val activityViewModel: MainViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val rootView = inflater.inflate(R.layout.fragment_test, container, false)

        checkBox2er = rootView.findViewById(R.id.checkbox2TT)
        checkBox3er = rootView.findViewById(R.id.checkbox3TT)
        // ...
        checkBox10er = rootView.findViewById(R.id.checkbox10TT)

        val checkBoxes = listOf(
            checkBox2er, checkBox3er, checkBox4er, checkBox5er,
            checkBox6er, checkBox7er, checkBox8er, checkBox9er, checkBox10er
        )

        // update CheckBox from ViewModel
        // IS THIS ACTUALLY NEEDED?
        lifecycleScope.launch {
            viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
                activityViewModel.testFactors.collect { updatedFactors: Set<Int> ->
                    Log.d(javaClass.simpleName, "testFactors was updated")
                    checkBoxes.forEach { checkBox ->
                        checkBox.tagNumber?.let { rowId -> checkBox.isChecked = updatedFactors.contains(rowId) }
                    }
                }
            }
        }

        // update ViewModel Set when a CheckBox is clicked
        checkBoxes.forEach { checkBox ->
            checkBox.setOnCheckedChangeListener { clickedCheckBox, isChecked ->
                clickedCheckBox.tagNumber?.let { rowId ->
                    Log.d(javaClass.simpleName, "clicked CheckBox $rowId")
                    activityViewModel.toggleCheckbox(rowId, isChecked)
                }
            }
        }

        return rootView
    }
}

val View.tagNumber: Int?
    get() {
        val stringTag = this.tag as? String
        return stringTag?.toIntOrNull()
    }

The layout of a CheckBox looks like this:

<CheckBox
    android:id="@+id/checkbox2TT"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="start"
    android:checked="true"
    android:text="@string/TT2"
    android:tag="2" />

I manage a Set of all currently selected CheckBoxes in my ViewModel:

class MainViewModel(application: Application) : AndroidViewModel(application) {

    private val writableTestFactors: MutableStateFlow<Set<Int>> = MutableStateFlow(sortedSetOf(2,3,4,5,6,7,8,9,10))
    val testFactors: StateFlow<Set<Int>> = writableTestFactors

    fun toggleCheckbox(toggleRow: Int, isChecked: Boolean) {
        val newSortedSet = TreeSet(testFactors.value)
        if (isChecked) {
            newSortedSet.add(toggleRow)
        } else {
            newSortedSet.remove(toggleRow)
        }
        writableTestFactors.value = newSortedSet
    }
}

I am now confused whether the lifecycleScope.launch {} is actually needed. The ViewModel should be the single source of truth in MVVM according to my understanding, but it seems like the Fragment stores and restores the checked state of the CheckBoxes itself when the application process gets killed.

What should be the single source of truth here, and how would I properly implement a MVVM approach?

Upvotes: 0

Views: 30

Answers (0)

Related Questions