diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
index 017306875c..7366e2c778 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GamePropertiesAdapter.kt
@@ -6,11 +6,7 @@ package org.yuzu.yuzu_emu.adapters
 import android.view.LayoutInflater
 import android.view.ViewGroup
 import androidx.core.content.res.ResourcesCompat
-import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.databinding.CardInstallableIconBinding
 import org.yuzu.yuzu_emu.databinding.CardSimpleOutlinedBinding
 import org.yuzu.yuzu_emu.model.GameProperty
@@ -18,6 +14,7 @@ import org.yuzu.yuzu_emu.model.InstallableProperty
 import org.yuzu.yuzu_emu.model.SubmenuProperty
 import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
 import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
 import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 
 class GamePropertiesAdapter(
@@ -82,11 +79,7 @@ class GamePropertiesAdapter(
                 binding.details.text = submenuProperty.details.invoke()
             } else if (submenuProperty.detailsFlow != null) {
                 binding.details.setVisible(true)
-                viewLifecycle.lifecycleScope.launch {
-                    viewLifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
-                        submenuProperty.detailsFlow.collect { binding.details.text = it }
-                    }
-                }
+                submenuProperty.detailsFlow.collect(viewLifecycle) { binding.details.text = it }
             } else {
                 binding.details.setVisible(false)
             }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
index 9234a49014..0bd196673c 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/HomeSettingAdapter.kt
@@ -8,17 +8,14 @@ import android.view.ViewGroup
 import androidx.appcompat.app.AppCompatActivity
 import androidx.core.content.ContextCompat
 import androidx.core.content.res.ResourcesCompat
-import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.CardHomeOptionBinding
 import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
 import org.yuzu.yuzu_emu.model.HomeSetting
 import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
 import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
 import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 
 class HomeSettingAdapter(
@@ -59,11 +56,7 @@ class HomeSettingAdapter(
                 binding.optionIcon.alpha = 0.5f
             }
 
-            viewLifecycle.lifecycleScope.launch {
-                viewLifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    model.details.collect { updateOptionDetails(it) }
-                }
-            }
+            model.details.collect(viewLifecycle) { updateOptionDetails(it) }
             binding.optionDetail.marquee()
 
             binding.root.setOnClickListener { onClick(model) }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt
index 9b24d41c16..1bae593ae0 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/InputProfileDialogFragment.kt
@@ -11,16 +11,13 @@ import android.view.ViewGroup
 import android.widget.Toast
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.recyclerview.widget.LinearLayoutManager
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.DialogInputProfilesBinding
 import org.yuzu.yuzu_emu.features.settings.model.view.InputProfileSetting
 import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
+import org.yuzu.yuzu_emu.utils.collect
 
 class InputProfileDialogFragment : DialogFragment() {
     private var position = 0
@@ -110,25 +107,21 @@ class InputProfileDialogFragment : DialogFragment() {
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
 
-        viewLifecycleOwner.lifecycleScope.launch {
-            repeatOnLifecycle(Lifecycle.State.CREATED) {
-                settingsViewModel.shouldShowDeleteProfileDialog.collect {
-                    if (it.isNotEmpty()) {
-                        MessageDialogFragment.newInstance(
-                            activity = requireActivity(),
-                            titleId = R.string.delete_input_profile,
-                            descriptionId = R.string.delete_input_profile_description,
-                            positiveAction = {
-                                setting.deleteProfile(it)
-                                settingsViewModel.setReloadListAndNotifyDataset(true)
-                            },
-                            negativeAction = {},
-                            negativeButtonTitleId = android.R.string.cancel
-                        ).show(parentFragmentManager, MessageDialogFragment.TAG)
-                        settingsViewModel.setShouldShowDeleteProfileDialog("")
-                        dismiss()
-                    }
-                }
+        settingsViewModel.shouldShowDeleteProfileDialog.collect(viewLifecycleOwner) {
+            if (it.isNotEmpty()) {
+                MessageDialogFragment.newInstance(
+                    activity = requireActivity(),
+                    titleId = R.string.delete_input_profile,
+                    descriptionId = R.string.delete_input_profile_description,
+                    positiveAction = {
+                        setting.deleteProfile(it)
+                        settingsViewModel.setReloadListAndNotifyDataset(true)
+                    },
+                    negativeAction = {},
+                    negativeButtonTitleId = android.R.string.cancel
+                ).show(parentFragmentManager, MessageDialogFragment.TAG)
+                settingsViewModel.setShouldShowDeleteProfileDialog("")
+                dismiss()
             }
         }
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
index 681a18b3b1..455b3b5ff1 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt
@@ -13,14 +13,9 @@ import androidx.appcompat.app.AppCompatActivity
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowCompat
 import androidx.core.view.WindowInsetsCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.fragment.NavHostFragment
 import androidx.navigation.navArgs
 import com.google.android.material.color.MaterialColors
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.NativeLibrary
 import java.io.IOException
 import org.yuzu.yuzu_emu.R
@@ -70,39 +65,23 @@ class SettingsActivity : AppCompatActivity() {
             )
         }
 
-        lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    settingsViewModel.shouldRecreate.collectLatest {
-                        if (it) {
-                            settingsViewModel.setShouldRecreate(false)
-                            recreate()
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    settingsViewModel.shouldNavigateBack.collectLatest {
-                        if (it) {
-                            settingsViewModel.setShouldNavigateBack(false)
-                            navigateBack()
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    settingsViewModel.shouldShowResetSettingsDialog.collectLatest {
-                        if (it) {
-                            settingsViewModel.setShouldShowResetSettingsDialog(false)
-                            ResetSettingsDialogFragment().show(
-                                supportFragmentManager,
-                                ResetSettingsDialogFragment.TAG
-                            )
-                        }
-                    }
-                }
+        settingsViewModel.shouldRecreate.collect(
+            this,
+            resetState = { settingsViewModel.setShouldRecreate(false) }
+        ) { if (it) recreate() }
+        settingsViewModel.shouldNavigateBack.collect(
+            this,
+            resetState = { settingsViewModel.setShouldNavigateBack(false) }
+        ) { if (it) navigateBack() }
+        settingsViewModel.shouldShowResetSettingsDialog.collect(
+            this,
+            resetState = { settingsViewModel.setShouldShowResetSettingsDialog(false) }
+        ) {
+            if (it) {
+                ResetSettingsDialogFragment().show(
+                    supportFragmentManager,
+                    ResetSettingsDialogFragment.TAG
+                )
             }
         }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
index 5d1ea5d295..a81ff6b1a9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsDialogFragment.kt
@@ -11,12 +11,8 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.slider.Slider
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
 import org.yuzu.yuzu_emu.features.input.NativeInput
@@ -29,6 +25,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
 import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
 import org.yuzu.yuzu_emu.utils.ParamPackage
+import org.yuzu.yuzu_emu.utils.collect
 
 class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener {
     private var type = 0
@@ -169,17 +166,11 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
         super.onViewCreated(view, savedInstanceState)
         when (type) {
             SettingsItem.TYPE_SLIDER -> {
-                viewLifecycleOwner.lifecycleScope.launch {
-                    repeatOnLifecycle(Lifecycle.State.CREATED) {
-                        settingsViewModel.sliderTextValue.collect {
-                            sliderBinding.textValue.text = it
-                        }
-                    }
-                    repeatOnLifecycle(Lifecycle.State.CREATED) {
-                        settingsViewModel.sliderProgress.collect {
-                            sliderBinding.slider.value = it.toFloat()
-                        }
-                    }
+                settingsViewModel.sliderTextValue.collect(viewLifecycleOwner) {
+                    sliderBinding.textValue.text = it
+                }
+                settingsViewModel.sliderProgress.collect(viewLifecycleOwner) {
+                    sliderBinding.slider.value = it.toFloat()
                 }
             }
         }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
index 0cf944b431..ec16f16c46 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragment.kt
@@ -13,21 +13,17 @@ import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.updatePadding
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.findNavController
 import androidx.navigation.fragment.navArgs
 import androidx.recyclerview.widget.LinearLayoutManager
 import com.google.android.material.transition.MaterialSharedAxis
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.FragmentSettingsBinding
 import org.yuzu.yuzu_emu.features.input.NativeInput
 import org.yuzu.yuzu_emu.features.settings.model.Settings
 import org.yuzu.yuzu_emu.fragments.MessageDialogFragment
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
 
 class SettingsFragment : Fragment() {
     private lateinit var presenter: SettingsFragmentPresenter
@@ -63,8 +59,7 @@ class SettingsFragment : Fragment() {
         return binding.root
     }
 
-    // This is using the correct scope, lint is just acting up
-    @SuppressLint("UnsafeRepeatOnLifecycleDetector", "NotifyDataSetChanged")
+    @SuppressLint("NotifyDataSetChanged")
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         settingsAdapter = SettingsAdapter(this, requireContext())
@@ -100,65 +95,37 @@ class SettingsFragment : Fragment() {
             settingsViewModel.setShouldNavigateBack(true)
         }
 
-        viewLifecycleOwner.lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    settingsViewModel.shouldReloadSettingsList.collectLatest {
-                        if (it) {
-                            settingsViewModel.setShouldReloadSettingsList(false)
-                            presenter.loadSettingsList()
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    settingsViewModel.adapterItemChanged.collect {
-                        if (it != -1) {
-                            settingsAdapter?.notifyItemChanged(it)
-                            settingsViewModel.setAdapterItemChanged(-1)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    settingsViewModel.datasetChanged.collect {
-                        if (it) {
-                            settingsAdapter?.notifyDataSetChanged()
-                            settingsViewModel.setDatasetChanged(false)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    settingsViewModel.reloadListAndNotifyDataset.collectLatest {
-                        if (it) {
-                            settingsViewModel.setReloadListAndNotifyDataset(false)
-                            presenter.loadSettingsList(true)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    settingsViewModel.shouldShowResetInputDialog.collectLatest {
-                        if (it) {
-                            MessageDialogFragment.newInstance(
-                                activity = requireActivity(),
-                                titleId = R.string.reset_mapping,
-                                descriptionId = R.string.reset_mapping_description,
-                                positiveAction = {
-                                    NativeInput.resetControllerMappings(getPlayerIndex())
-                                    settingsViewModel.setReloadListAndNotifyDataset(true)
-                                },
-                                negativeAction = {}
-                            ).show(parentFragmentManager, MessageDialogFragment.TAG)
-                            settingsViewModel.setShouldShowResetInputDialog(false)
-                        }
-                    }
-                }
+        settingsViewModel.shouldReloadSettingsList.collect(
+            viewLifecycleOwner,
+            resetState = { settingsViewModel.setShouldReloadSettingsList(false) }
+        ) { if (it) presenter.loadSettingsList() }
+        settingsViewModel.adapterItemChanged.collect(
+            viewLifecycleOwner,
+            resetState = { settingsViewModel.setAdapterItemChanged(-1) }
+        ) { if (it != -1) settingsAdapter?.notifyItemChanged(it) }
+        settingsViewModel.datasetChanged.collect(
+            viewLifecycleOwner,
+            resetState = { settingsViewModel.setDatasetChanged(false) }
+        ) { if (it) settingsAdapter?.notifyDataSetChanged() }
+        settingsViewModel.reloadListAndNotifyDataset.collect(
+            viewLifecycleOwner,
+            resetState = { settingsViewModel.setReloadListAndNotifyDataset(false) }
+        ) { if (it) presenter.loadSettingsList(true) }
+        settingsViewModel.shouldShowResetInputDialog.collect(
+            viewLifecycleOwner,
+            resetState = { settingsViewModel.setShouldShowResetInputDialog(false) }
+        ) {
+            if (it) {
+                MessageDialogFragment.newInstance(
+                    activity = requireActivity(),
+                    titleId = R.string.reset_mapping,
+                    descriptionId = R.string.reset_mapping_description,
+                    positiveAction = {
+                        NativeInput.resetControllerMappings(getPlayerIndex())
+                        settingsViewModel.setReloadListAndNotifyDataset(true)
+                    },
+                    negativeAction = {}
+                ).show(parentFragmentManager, MessageDialogFragment.TAG)
             }
         }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
index c4c1d563ad..ed60cf34f9 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsSearchFragment.kt
@@ -15,20 +15,17 @@ import androidx.core.view.updatePadding
 import androidx.core.widget.doOnTextChanged
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.recyclerview.widget.LinearLayoutManager
 import com.google.android.material.divider.MaterialDividerItemDecoration
 import com.google.android.material.transition.MaterialSharedAxis
 import info.debatty.java.stringsimilarity.Cosine
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.FragmentSettingsSearchBinding
 import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
 import org.yuzu.yuzu_emu.utils.NativeConfig
 import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
 
 class SettingsSearchFragment : Fragment() {
     private var _binding: FragmentSettingsSearchBinding? = null
@@ -84,14 +81,10 @@ class SettingsSearchFragment : Fragment() {
             search()
             binding.settingsList.smoothScrollToPosition(0)
         }
-        viewLifecycleOwner.lifecycleScope.launch {
-            repeatOnLifecycle(Lifecycle.State.CREATED) {
-                settingsViewModel.shouldReloadSettingsList.collect {
-                    if (it) {
-                        settingsViewModel.setShouldReloadSettingsList(false)
-                        search()
-                    }
-                }
+        settingsViewModel.shouldReloadSettingsList.collect(viewLifecycleOwner) {
+            if (it) {
+                settingsViewModel.setShouldReloadSettingsList(false)
+                search()
             }
         }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
index 872553ac43..110aa29600 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/AddonsFragment.kt
@@ -3,7 +3,6 @@
 
 package org.yuzu.yuzu_emu.fragments
 
-import android.annotation.SuppressLint
 import android.content.Intent
 import android.os.Bundle
 import android.view.LayoutInflater
@@ -16,9 +15,6 @@ import androidx.core.view.updatePadding
 import androidx.documentfile.provider.DocumentFile
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.findNavController
 import androidx.navigation.fragment.navArgs
 import androidx.recyclerview.widget.LinearLayoutManager
@@ -32,6 +28,7 @@ import org.yuzu.yuzu_emu.model.HomeViewModel
 import org.yuzu.yuzu_emu.utils.AddonUtil
 import org.yuzu.yuzu_emu.utils.FileUtil.copyFilesTo
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
 import java.io.File
 
 class AddonsFragment : Fragment() {
@@ -60,8 +57,6 @@ class AddonsFragment : Fragment() {
         return binding.root
     }
 
-    // This is using the correct scope, lint is just acting up
-    @SuppressLint("UnsafeRepeatOnLifecycleDetector")
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         homeViewModel.setNavigationVisibility(visible = false, animated = false)
@@ -78,57 +73,41 @@ class AddonsFragment : Fragment() {
             adapter = AddonAdapter(addonViewModel)
         }
 
-        viewLifecycleOwner.lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    addonViewModel.addonList.collect {
-                        (binding.listAddons.adapter as AddonAdapter).submitList(it)
-                    }
-                }
+        addonViewModel.addonList.collect(viewLifecycleOwner) {
+            (binding.listAddons.adapter as AddonAdapter).submitList(it)
+        }
+        addonViewModel.showModInstallPicker.collect(
+            viewLifecycleOwner,
+            resetState = { addonViewModel.showModInstallPicker(false) }
+        ) { if (it) installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data) }
+        addonViewModel.showModNoticeDialog.collect(
+            viewLifecycleOwner,
+            resetState = { addonViewModel.showModNoticeDialog(false) }
+        ) {
+            if (it) {
+                MessageDialogFragment.newInstance(
+                    requireActivity(),
+                    titleId = R.string.addon_notice,
+                    descriptionId = R.string.addon_notice_description,
+                    dismissible = false,
+                    positiveAction = { addonViewModel.showModInstallPicker(true) },
+                    negativeAction = {},
+                    negativeButtonTitleId = R.string.close
+                ).show(parentFragmentManager, MessageDialogFragment.TAG)
             }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    addonViewModel.showModInstallPicker.collect {
-                        if (it) {
-                            installAddon.launch(Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).data)
-                            addonViewModel.showModInstallPicker(false)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    addonViewModel.showModNoticeDialog.collect {
-                        if (it) {
-                            MessageDialogFragment.newInstance(
-                                requireActivity(),
-                                titleId = R.string.addon_notice,
-                                descriptionId = R.string.addon_notice_description,
-                                dismissible = false,
-                                positiveAction = { addonViewModel.showModInstallPicker(true) },
-                                negativeAction = {},
-                                negativeButtonTitleId = R.string.close
-                            ).show(parentFragmentManager, MessageDialogFragment.TAG)
-                            addonViewModel.showModNoticeDialog(false)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    addonViewModel.addonToDelete.collect {
-                        if (it != null) {
-                            MessageDialogFragment.newInstance(
-                                requireActivity(),
-                                titleId = R.string.confirm_uninstall,
-                                descriptionId = R.string.confirm_uninstall_description,
-                                positiveAction = { addonViewModel.onDeleteAddon(it) },
-                                negativeAction = {}
-                            ).show(parentFragmentManager, MessageDialogFragment.TAG)
-                            addonViewModel.setAddonToDelete(null)
-                        }
-                    }
-                }
+        }
+        addonViewModel.addonToDelete.collect(
+            viewLifecycleOwner,
+            resetState = { addonViewModel.setAddonToDelete(null) }
+        ) {
+            if (it != null) {
+                MessageDialogFragment.newInstance(
+                    requireActivity(),
+                    titleId = R.string.confirm_uninstall,
+                    descriptionId = R.string.confirm_uninstall_description,
+                    positiveAction = { addonViewModel.onDeleteAddon(it) },
+                    negativeAction = {}
+                ).show(parentFragmentManager, MessageDialogFragment.TAG)
             }
         }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
index 41cff46c17..8b23a10217 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriverManagerFragment.kt
@@ -3,7 +3,6 @@
 
 package org.yuzu.yuzu_emu.fragments
 
-import android.annotation.SuppressLint
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
@@ -14,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.updatePadding
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.findNavController
 import androidx.navigation.fragment.navArgs
 import androidx.recyclerview.widget.GridLayoutManager
@@ -35,6 +31,7 @@ import org.yuzu.yuzu_emu.utils.FileUtil
 import org.yuzu.yuzu_emu.utils.GpuDriverHelper
 import org.yuzu.yuzu_emu.utils.NativeConfig
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
 import java.io.File
 import java.io.IOException
 
@@ -63,8 +60,6 @@ class DriverManagerFragment : Fragment() {
         return binding.root
     }
 
-    // This is using the correct scope, lint is just acting up
-    @SuppressLint("UnsafeRepeatOnLifecycleDetector")
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         homeViewModel.setNavigationVisibility(visible = false, animated = true)
@@ -89,15 +84,8 @@ class DriverManagerFragment : Fragment() {
                 }
             }
 
-            viewLifecycleOwner.lifecycleScope.apply {
-                launch {
-                    repeatOnLifecycle(Lifecycle.State.STARTED) {
-                        driverViewModel.showClearButton.collect {
-                            binding.toolbarDrivers.menu
-                                .findItem(R.id.menu_driver_use_global).isVisible = it
-                        }
-                    }
-                }
+            driverViewModel.showClearButton.collect(viewLifecycleOwner) {
+                binding.toolbarDrivers.menu.findItem(R.id.menu_driver_use_global).isVisible = it
             }
         }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
index 6a47b29f0d..bad56e434a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/DriversLoadingDialogFragment.kt
@@ -10,14 +10,11 @@ import android.view.View
 import android.view.ViewGroup
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
 import org.yuzu.yuzu_emu.model.DriverViewModel
+import org.yuzu.yuzu_emu.utils.collect
 
 class DriversLoadingDialogFragment : DialogFragment() {
     private val driverViewModel: DriverViewModel by activityViewModels()
@@ -44,13 +41,7 @@ class DriversLoadingDialogFragment : DialogFragment() {
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
-        viewLifecycleOwner.lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                    driverViewModel.isInteractionAllowed.collect { if (it) dismiss() }
-                }
-            }
-        }
+        driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) { if (it) dismiss() }
     }
 
     companion object {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
index aedc128d63..c3b2b11f80 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -32,9 +32,6 @@ import androidx.drawerlayout.widget.DrawerLayout
 import androidx.drawerlayout.widget.DrawerLayout.DrawerListener
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.findNavController
 import androidx.navigation.fragment.navArgs
 import androidx.window.layout.FoldingFeature
@@ -42,9 +39,6 @@ import androidx.window.layout.WindowInfoTracker
 import androidx.window.layout.WindowLayoutInfo
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.google.android.material.slider.Slider
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.HomeNavigationDirections
 import org.yuzu.yuzu_emu.NativeLibrary
 import org.yuzu.yuzu_emu.R
@@ -91,14 +85,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
         if (context is EmulationActivity) {
             emulationActivity = context
             NativeLibrary.setEmulationActivity(context)
-
-            lifecycleScope.launch(Dispatchers.Main) {
-                lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    WindowInfoTracker.getOrCreate(context)
-                        .windowLayoutInfo(context)
-                        .collect { updateFoldableLayout(context, it) }
-                }
-            }
         } else {
             throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
         }
@@ -169,8 +155,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
         return binding.root
     }
 
-    // This is using the correct scope, lint is just acting up
-    @SuppressLint("UnsafeRepeatOnLifecycleDetector")
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         if (requireActivity().isFinishing) {
@@ -351,129 +335,86 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
         binding.loadingTitle.isSelected = true
         binding.loadingText.isSelected = true
 
-        viewLifecycleOwner.lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    WindowInfoTracker.getOrCreate(requireContext())
-                        .windowLayoutInfo(requireActivity())
-                        .collect {
-                            updateFoldableLayout(requireActivity() as EmulationActivity, it)
-                        }
-                }
+        WindowInfoTracker.getOrCreate(requireContext())
+            .windowLayoutInfo(requireActivity()).collect(viewLifecycleOwner) {
+                updateFoldableLayout(requireActivity() as EmulationActivity, it)
             }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    emulationViewModel.shaderProgress.collectLatest {
-                        if (it > 0 && it != emulationViewModel.totalShaders.value) {
-                            binding.loadingProgressIndicator.isIndeterminate = false
+        emulationViewModel.shaderProgress.collect(viewLifecycleOwner) {
+            if (it > 0 && it != emulationViewModel.totalShaders.value) {
+                binding.loadingProgressIndicator.isIndeterminate = false
 
-                            if (it < binding.loadingProgressIndicator.max) {
-                                binding.loadingProgressIndicator.progress = it
-                            }
-                        }
+                if (it < binding.loadingProgressIndicator.max) {
+                    binding.loadingProgressIndicator.progress = it
+                }
+            }
 
-                        if (it == emulationViewModel.totalShaders.value) {
-                            binding.loadingText.setText(R.string.loading)
-                            binding.loadingProgressIndicator.isIndeterminate = true
-                        }
-                    }
-                }
+            if (it == emulationViewModel.totalShaders.value) {
+                binding.loadingText.setText(R.string.loading)
+                binding.loadingProgressIndicator.isIndeterminate = true
             }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    emulationViewModel.totalShaders.collectLatest {
-                        binding.loadingProgressIndicator.max = it
-                    }
-                }
+        }
+        emulationViewModel.totalShaders.collect(viewLifecycleOwner) {
+            binding.loadingProgressIndicator.max = it
+        }
+        emulationViewModel.shaderMessage.collect(viewLifecycleOwner) {
+            if (it.isNotEmpty()) {
+                binding.loadingText.text = it
             }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    emulationViewModel.shaderMessage.collectLatest {
-                        if (it.isNotEmpty()) {
-                            binding.loadingText.text = it
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                    driverViewModel.isInteractionAllowed.collect {
-                        if (it) {
-                            startEmulation()
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    emulationViewModel.emulationStarted.collectLatest {
-                        if (it) {
-                            binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
-                            ViewUtils.showView(binding.surfaceInputOverlay)
-                            ViewUtils.hideView(binding.loadingIndicator)
+        }
 
-                            emulationState.updateSurface()
+        emulationViewModel.emulationStarted.collect(viewLifecycleOwner) {
+            if (it) {
+                binding.drawerLayout.setDrawerLockMode(IntSetting.LOCK_DRAWER.getInt())
+                ViewUtils.showView(binding.surfaceInputOverlay)
+                ViewUtils.hideView(binding.loadingIndicator)
 
-                            // Setup overlays
-                            updateShowFpsOverlay()
-                            updateThermalOverlay()
-                        }
-                    }
-                }
+                emulationState.updateSurface()
+
+                // Setup overlays
+                updateShowFpsOverlay()
+                updateThermalOverlay()
             }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    emulationViewModel.isEmulationStopping.collectLatest {
-                        if (it) {
-                            binding.loadingText.setText(R.string.shutting_down)
-                            ViewUtils.showView(binding.loadingIndicator)
-                            ViewUtils.hideView(binding.inputContainer)
-                            ViewUtils.hideView(binding.showFpsText)
-                        }
-                    }
-                }
+        }
+        emulationViewModel.isEmulationStopping.collect(viewLifecycleOwner) {
+            if (it) {
+                binding.loadingText.setText(R.string.shutting_down)
+                ViewUtils.showView(binding.loadingIndicator)
+                ViewUtils.hideView(binding.inputContainer)
+                ViewUtils.hideView(binding.showFpsText)
             }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    emulationViewModel.drawerOpen.collect {
-                        if (it) {
-                            binding.drawerLayout.open()
-                            binding.inGameMenu.requestFocus()
-                        } else {
-                            binding.drawerLayout.close()
-                        }
-                    }
-                }
+        }
+        emulationViewModel.drawerOpen.collect(viewLifecycleOwner) {
+            if (it) {
+                binding.drawerLayout.open()
+                binding.inGameMenu.requestFocus()
+            } else {
+                binding.drawerLayout.close()
             }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    emulationViewModel.programChanged.collect {
-                        if (it != 0) {
-                            emulationViewModel.setEmulationStarted(false)
-                            binding.drawerLayout.close()
-                            binding.drawerLayout
-                                .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
-                            ViewUtils.hideView(binding.surfaceInputOverlay)
-                            ViewUtils.showView(binding.loadingIndicator)
-                        }
-                    }
-                }
+        }
+        emulationViewModel.programChanged.collect(viewLifecycleOwner) {
+            if (it != 0) {
+                emulationViewModel.setEmulationStarted(false)
+                binding.drawerLayout.close()
+                binding.drawerLayout
+                    .setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
+                ViewUtils.hideView(binding.surfaceInputOverlay)
+                ViewUtils.showView(binding.loadingIndicator)
             }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    emulationViewModel.emulationStopped.collect {
-                        if (it && emulationViewModel.programChanged.value != -1) {
-                            if (perfStatsUpdater != null) {
-                                perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
-                            }
-                            emulationState.changeProgram(emulationViewModel.programChanged.value)
-                            emulationViewModel.setProgramChanged(-1)
-                            emulationViewModel.setEmulationStopped(false)
-                        }
-                    }
+        }
+        emulationViewModel.emulationStopped.collect(viewLifecycleOwner) {
+            if (it && emulationViewModel.programChanged.value != -1) {
+                if (perfStatsUpdater != null) {
+                    perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
                 }
+                emulationState.changeProgram(emulationViewModel.programChanged.value)
+                emulationViewModel.setProgramChanged(-1)
+                emulationViewModel.setEmulationStopped(false)
             }
         }
+
+        driverViewModel.isInteractionAllowed.collect(viewLifecycleOwner) {
+            if (it) startEmulation()
+        }
     }
 
     private fun startEmulation(programIndex: Int = 0) {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
index 5c558b1a50..3a6f7a38c4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GameFoldersFragment.kt
@@ -13,9 +13,6 @@ import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.updatePadding
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.findNavController
 import androidx.recyclerview.widget.GridLayoutManager
 import com.google.android.material.transition.MaterialSharedAxis
@@ -27,6 +24,7 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
 import org.yuzu.yuzu_emu.model.HomeViewModel
 import org.yuzu.yuzu_emu.ui.main.MainActivity
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
 
 class GameFoldersFragment : Fragment() {
     private var _binding: FragmentFoldersBinding? = null
@@ -70,12 +68,8 @@ class GameFoldersFragment : Fragment() {
             adapter = FolderAdapter(requireActivity(), gamesViewModel)
         }
 
-        viewLifecycleOwner.lifecycleScope.launch {
-            repeatOnLifecycle(Lifecycle.State.CREATED) {
-                gamesViewModel.folders.collect {
-                    (binding.listFolders.adapter as FolderAdapter).submitList(it)
-                }
-            }
+        gamesViewModel.folders.collect(viewLifecycleOwner) {
+            (binding.listFolders.adapter as FolderAdapter).submitList(it)
         }
 
         val mainActivity = requireActivity() as MainActivity
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
index c4da1a65db..c06842c596 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/GamePropertiesFragment.kt
@@ -3,7 +3,6 @@
 
 package org.yuzu.yuzu_emu.fragments
 
-import android.annotation.SuppressLint
 import android.content.pm.ShortcutInfo
 import android.content.pm.ShortcutManager
 import android.os.Bundle
@@ -17,9 +16,7 @@ import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.updatePadding
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.findNavController
 import androidx.navigation.fragment.navArgs
 import androidx.recyclerview.widget.GridLayoutManager
@@ -47,6 +44,7 @@ import org.yuzu.yuzu_emu.utils.GpuDriverHelper
 import org.yuzu.yuzu_emu.utils.MemoryUtil
 import org.yuzu.yuzu_emu.utils.ViewUtils.marquee
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
 import java.io.BufferedOutputStream
 import java.io.File
 
@@ -76,8 +74,6 @@ class GamePropertiesFragment : Fragment() {
         return binding.root
     }
 
-    // This is using the correct scope, lint is just acting up
-    @SuppressLint("UnsafeRepeatOnLifecycleDetector")
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         homeViewModel.setNavigationVisibility(visible = false, animated = true)
@@ -116,28 +112,14 @@ class GamePropertiesFragment : Fragment() {
 
         reloadList()
 
-        viewLifecycleOwner.lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    homeViewModel.openImportSaves.collect {
-                        if (it) {
-                            importSaves.launch(arrayOf("application/zip"))
-                            homeViewModel.setOpenImportSaves(false)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.STARTED) {
-                    homeViewModel.reloadPropertiesList.collect {
-                        if (it) {
-                            reloadList()
-                            homeViewModel.reloadPropertiesList(false)
-                        }
-                    }
-                }
-            }
-        }
+        homeViewModel.openImportSaves.collect(
+            viewLifecycleOwner,
+            resetState = { homeViewModel.setOpenImportSaves(false) }
+        ) { if (it) importSaves.launch(arrayOf("application/zip")) }
+        homeViewModel.reloadPropertiesList.collect(
+            viewLifecycleOwner,
+            resetState = { homeViewModel.reloadPropertiesList(false) }
+        ) { if (it) reloadList() }
 
         setInsets()
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
index 63112dc6f0..d218da1c88 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/InstallableFragment.kt
@@ -14,9 +14,6 @@ import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.updatePadding
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.findNavController
 import androidx.recyclerview.widget.GridLayoutManager
 import com.google.android.material.transition.MaterialSharedAxis
@@ -35,6 +32,7 @@ import org.yuzu.yuzu_emu.ui.main.MainActivity
 import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 import org.yuzu.yuzu_emu.utils.FileUtil
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
 import java.io.BufferedOutputStream
 import java.io.File
 import java.math.BigInteger
@@ -75,14 +73,10 @@ class InstallableFragment : Fragment() {
             binding.root.findNavController().popBackStack()
         }
 
-        viewLifecycleOwner.lifecycleScope.launch {
-            repeatOnLifecycle(Lifecycle.State.CREATED) {
-                homeViewModel.openImportSaves.collect {
-                    if (it) {
-                        importSaves.launch(arrayOf("application/zip"))
-                        homeViewModel.setOpenImportSaves(false)
-                    }
-                }
+        homeViewModel.openImportSaves.collect(viewLifecycleOwner) {
+            if (it) {
+                importSaves.launch(arrayOf("application/zip"))
+                homeViewModel.setOpenImportSaves(false)
             }
         }
 
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
index ae29e9cd10..ee3bb0386a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/ProgressDialogFragment.kt
@@ -13,16 +13,13 @@ import androidx.appcompat.app.AlertDialog
 import androidx.fragment.app.DialogFragment
 import androidx.fragment.app.FragmentActivity
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.databinding.DialogProgressBarBinding
 import org.yuzu.yuzu_emu.model.TaskViewModel
 import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
 
 class ProgressDialogFragment : DialogFragment() {
     private val taskViewModel: TaskViewModel by activityViewModels()
@@ -65,70 +62,50 @@ class ProgressDialogFragment : DialogFragment() {
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         binding.message.isSelected = true
-        viewLifecycleOwner.lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    taskViewModel.isComplete.collect {
-                        if (it) {
-                            dismiss()
-                            when (val result = taskViewModel.result.value) {
-                                is String -> Toast.makeText(
-                                    requireContext(),
-                                    result,
-                                    Toast.LENGTH_LONG
-                                ).show()
+        taskViewModel.isComplete.collect(viewLifecycleOwner) {
+            if (it) {
+                dismiss()
+                when (val result = taskViewModel.result.value) {
+                    is String -> Toast.makeText(
+                        requireContext(),
+                        result,
+                        Toast.LENGTH_LONG
+                    ).show()
 
-                                is MessageDialogFragment -> result.show(
-                                    requireActivity().supportFragmentManager,
-                                    MessageDialogFragment.TAG
-                                )
+                    is MessageDialogFragment -> result.show(
+                        requireActivity().supportFragmentManager,
+                        MessageDialogFragment.TAG
+                    )
 
-                                else -> {
-                                    // Do nothing
-                                }
-                            }
-                            taskViewModel.clear()
-                        }
+                    else -> {
+                        // Do nothing
                     }
                 }
+                taskViewModel.clear()
             }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    taskViewModel.cancelled.collect {
-                        if (it) {
-                            dialog?.setTitle(R.string.cancelling)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    taskViewModel.progress.collect {
-                        if (it != 0.0) {
-                            binding.progressBar.apply {
-                                isIndeterminate = false
-                                progress = (
-                                    (it / taskViewModel.maxProgress.value) *
-                                        PROGRESS_BAR_RESOLUTION
-                                    ).toInt()
-                                min = 0
-                                max = PROGRESS_BAR_RESOLUTION
-                            }
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    taskViewModel.message.collect {
-                        binding.message.setVisible(it.isNotEmpty())
-                        if (it.isNotEmpty()) {
-                            binding.message.text = it
-                        }
-                    }
+        }
+        taskViewModel.cancelled.collect(viewLifecycleOwner) {
+            if (it) {
+                dialog?.setTitle(R.string.cancelling)
+            }
+        }
+        taskViewModel.progress.collect(viewLifecycleOwner) {
+            if (it != 0.0) {
+                binding.progressBar.apply {
+                    isIndeterminate = false
+                    progress = (
+                        (it / taskViewModel.maxProgress.value) *
+                            PROGRESS_BAR_RESOLUTION
+                        ).toInt()
+                    min = 0
+                    max = PROGRESS_BAR_RESOLUTION
                 }
             }
         }
+        taskViewModel.message.collect(viewLifecycleOwner) {
+            binding.message.setVisible(it.isNotEmpty())
+            binding.message.text = it
+        }
     }
 
     // By default, the ProgressDialog will immediately dismiss itself upon a button being pressed.
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
index 9f65096052..662ae9760a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SearchFragment.kt
@@ -3,7 +3,6 @@
 
 package org.yuzu.yuzu_emu.fragments
 
-import android.annotation.SuppressLint
 import android.content.Context
 import android.content.SharedPreferences
 import android.os.Bundle
@@ -18,14 +17,9 @@ import androidx.core.view.updatePadding
 import androidx.core.widget.doOnTextChanged
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.preference.PreferenceManager
 import info.debatty.java.stringsimilarity.Jaccard
 import info.debatty.java.stringsimilarity.JaroWinkler
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
 import java.util.Locale
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.YuzuApplication
@@ -36,6 +30,7 @@ import org.yuzu.yuzu_emu.model.Game
 import org.yuzu.yuzu_emu.model.GamesViewModel
 import org.yuzu.yuzu_emu.model.HomeViewModel
 import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
 
 class SearchFragment : Fragment() {
     private var _binding: FragmentSearchBinding? = null
@@ -59,8 +54,6 @@ class SearchFragment : Fragment() {
         return binding.root
     }
 
-    // This is using the correct scope, lint is just acting up
-    @SuppressLint("UnsafeRepeatOnLifecycleDetector")
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         homeViewModel.setNavigationVisibility(visible = true, animated = true)
@@ -86,30 +79,14 @@ class SearchFragment : Fragment() {
             filterAndSearch()
         }
 
-        viewLifecycleOwner.lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    gamesViewModel.searchFocused.collect {
-                        if (it) {
-                            focusSearch()
-                            gamesViewModel.setSearchFocused(false)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    gamesViewModel.games.collectLatest { filterAndSearch() }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    gamesViewModel.searchedGames.collect {
-                        (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
-                        binding.noResultsView.setVisible(it.isEmpty())
-                    }
-                }
-            }
+        gamesViewModel.searchFocused.collect(
+            viewLifecycleOwner,
+            resetState = { gamesViewModel.setSearchFocused(false) }
+        ) { if (it) focusSearch() }
+        gamesViewModel.games.collect(viewLifecycleOwner) { filterAndSearch() }
+        gamesViewModel.searchedGames.collect(viewLifecycleOwner) {
+            (binding.gridGamesSearch.adapter as GameAdapter).submitList(it)
+            binding.noResultsView.setVisible(it.isNotEmpty())
         }
 
         binding.clearButton.setOnClickListener { binding.searchText.setText("") }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
index eb279d309f..4f7548e98e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/SetupFragment.kt
@@ -4,7 +4,6 @@
 package org.yuzu.yuzu_emu.fragments
 
 import android.Manifest
-import android.annotation.SuppressLint
 import android.content.Intent
 import android.os.Build
 import android.os.Bundle
@@ -23,9 +22,6 @@ import androidx.core.view.isVisible
 import androidx.core.view.updatePadding
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.findNavController
 import androidx.preference.PreferenceManager
 import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
@@ -47,6 +43,7 @@ import org.yuzu.yuzu_emu.utils.DirectoryInitialization
 import org.yuzu.yuzu_emu.utils.NativeConfig
 import org.yuzu.yuzu_emu.utils.ViewUtils
 import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
+import org.yuzu.yuzu_emu.utils.collect
 
 class SetupFragment : Fragment() {
     private var _binding: FragmentSetupBinding? = null
@@ -78,8 +75,6 @@ class SetupFragment : Fragment() {
         return binding.root
     }
 
-    // This is using the correct scope, lint is just acting up
-    @SuppressLint("UnsafeRepeatOnLifecycleDetector")
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         mainActivity = requireActivity() as MainActivity
 
@@ -211,28 +206,14 @@ class SetupFragment : Fragment() {
             )
         }
 
-        viewLifecycleOwner.lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    homeViewModel.shouldPageForward.collect {
-                        if (it) {
-                            pageForward()
-                            homeViewModel.setShouldPageForward(false)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    homeViewModel.gamesDirSelected.collect {
-                        if (it) {
-                            gamesDirCallback.onStepCompleted()
-                            homeViewModel.setGamesDirSelected(false)
-                        }
-                    }
-                }
-            }
-        }
+        homeViewModel.shouldPageForward.collect(
+            viewLifecycleOwner,
+            resetState = { homeViewModel.setShouldPageForward(false) }
+        ) { if (it) pageForward() }
+        homeViewModel.gamesDirSelected.collect(
+            viewLifecycleOwner,
+            resetState = { homeViewModel.setGamesDirSelected(false) }
+        ) { if (it) gamesDirCallback.onStepCompleted() }
 
         binding.viewPager2.apply {
             adapter = SetupAdapter(requireActivity() as AppCompatActivity, pages)
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
index b3248585e6..fadb20e394 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/GamesFragment.kt
@@ -3,7 +3,6 @@
 
 package org.yuzu.yuzu_emu.ui
 
-import android.annotation.SuppressLint
 import android.os.Bundle
 import android.view.LayoutInflater
 import android.view.View
@@ -14,12 +13,7 @@ import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.updatePadding
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import com.google.android.material.color.MaterialColors
-import kotlinx.coroutines.flow.collectLatest
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.adapters.GameAdapter
 import org.yuzu.yuzu_emu.databinding.FragmentGamesBinding
@@ -28,6 +22,7 @@ import org.yuzu.yuzu_emu.model.GamesViewModel
 import org.yuzu.yuzu_emu.model.HomeViewModel
 import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
 import org.yuzu.yuzu_emu.utils.ViewUtils.updateMargins
+import org.yuzu.yuzu_emu.utils.collect
 
 class GamesFragment : Fragment() {
     private var _binding: FragmentGamesBinding? = null
@@ -45,8 +40,6 @@ class GamesFragment : Fragment() {
         return binding.root
     }
 
-    // This is using the correct scope, lint is just acting up
-    @SuppressLint("UnsafeRepeatOnLifecycleDetector")
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
         homeViewModel.setNavigationVisibility(visible = true, animated = true)
@@ -89,48 +82,28 @@ class GamesFragment : Fragment() {
             }
         }
 
-        viewLifecycleOwner.lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                    gamesViewModel.isReloading.collect {
-                        binding.swipeRefresh.isRefreshing = it
-                        binding.noticeText.setVisible(
-                            visible = gamesViewModel.games.value.isEmpty() && !it,
-                            gone = false
-                        )
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                    gamesViewModel.games.collectLatest {
-                        (binding.gridGames.adapter as GameAdapter).submitList(it)
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                    gamesViewModel.shouldSwapData.collect {
-                        if (it) {
-                            (binding.gridGames.adapter as GameAdapter).submitList(
-                                gamesViewModel.games.value
-                            )
-                            gamesViewModel.setShouldSwapData(false)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                    gamesViewModel.shouldScrollToTop.collect {
-                        if (it) {
-                            scrollToTop()
-                            gamesViewModel.setShouldScrollToTop(false)
-                        }
-                    }
-                }
+        gamesViewModel.isReloading.collect(viewLifecycleOwner) {
+            binding.swipeRefresh.isRefreshing = it
+            binding.noticeText.setVisible(
+                visible = gamesViewModel.games.value.isEmpty() && !it,
+                gone = false
+            )
+        }
+        gamesViewModel.games.collect(viewLifecycleOwner) {
+            (binding.gridGames.adapter as GameAdapter).submitList(it)
+        }
+        gamesViewModel.shouldSwapData.collect(
+            viewLifecycleOwner,
+            resetState = { gamesViewModel.setShouldSwapData(false) }
+        ) {
+            if (it) {
+                (binding.gridGames.adapter as GameAdapter).submitList(gamesViewModel.games.value)
             }
         }
+        gamesViewModel.shouldScrollToTop.collect(
+            viewLifecycleOwner,
+            resetState = { gamesViewModel.setShouldScrollToTop(false) }
+        ) { if (it) scrollToTop() }
 
         setInsets()
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
index 703fbaf3e7..d16f8a931e 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.kt
@@ -19,9 +19,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
 import androidx.core.view.ViewCompat
 import androidx.core.view.WindowCompat
 import androidx.core.view.WindowInsetsCompat
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
 import androidx.navigation.NavController
 import androidx.navigation.fragment.NavHostFragment
 import androidx.navigation.ui.setupWithNavController
@@ -30,7 +27,6 @@ import com.google.android.material.color.MaterialColors
 import com.google.android.material.navigation.NavigationBarView
 import java.io.File
 import java.io.FilenameFilter
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.HomeNavigationDirections
 import org.yuzu.yuzu_emu.NativeLibrary
 import org.yuzu.yuzu_emu.R
@@ -144,38 +140,19 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
             binding.statusBarShade.setVisible(visible = false, gone = false)
         }
 
-        lifecycleScope.apply {
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    homeViewModel.navigationVisible.collect { showNavigation(it.first, it.second) }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    homeViewModel.statusBarShadeVisible.collect { showStatusBarShade(it) }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    homeViewModel.contentToInstall.collect {
-                        if (it != null) {
-                            installContent(it)
-                            homeViewModel.setContentToInstall(null)
-                        }
-                    }
-                }
-            }
-            launch {
-                repeatOnLifecycle(Lifecycle.State.CREATED) {
-                    homeViewModel.checkKeys.collect {
-                        if (it) {
-                            checkKeys()
-                            homeViewModel.setCheckKeys(false)
-                        }
-                    }
-                }
+        homeViewModel.navigationVisible.collect(this) { showNavigation(it.first, it.second) }
+        homeViewModel.statusBarShadeVisible.collect(this) { showStatusBarShade(it) }
+        homeViewModel.contentToInstall.collect(
+            this,
+            resetState = { homeViewModel.setContentToInstall(null) }
+        ) {
+            if (it != null) {
+                installContent(it)
             }
         }
+        homeViewModel.checkKeys.collect(this, resetState = { homeViewModel.setCheckKeys(false) }) {
+            if (it) checkKeys()
+        }
 
         setInsets()
     }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt
new file mode 100644
index 0000000000..d5c19c681e
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/LifecycleUtils.kt
@@ -0,0 +1,38 @@
+// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.launch
+
+/**
+ * Collects this [Flow] with a given [LifecycleOwner].
+ * @param scope [LifecycleOwner] that this [Flow] will be collected with.
+ * @param repeatState When to repeat collection on this [Flow].
+ * @param resetState Optional lambda to reset state of an underlying [MutableStateFlow] after
+ * [stateCollector] has been run.
+ * @param stateCollector Lambda that receives new state.
+ */
+inline fun <reified T> Flow<T>.collect(
+    scope: LifecycleOwner,
+    repeatState: Lifecycle.State = Lifecycle.State.CREATED,
+    crossinline resetState: () -> Unit = {},
+    crossinline stateCollector: (state: T) -> Unit
+) {
+    scope.apply {
+        lifecycleScope.launch {
+            repeatOnLifecycle(repeatState) {
+                this@collect.collect {
+                    stateCollector(it)
+                    resetState()
+                }
+            }
+        }
+    }
+}