Skip to content

Instantly share code, notes, and snippets.

@akexorcist
Created March 12, 2025 06:40
Show Gist options
  • Save akexorcist/581fef399ae0d7aefa67af51a2eb865c to your computer and use it in GitHub Desktop.
Save akexorcist/581fef399ae0d7aefa67af51a2eb865c to your computer and use it in GitHub Desktop.

Revisions

  1. akexorcist created this gist Mar 12, 2025.
    34 changes: 34 additions & 0 deletions ComposableScopeViewModelProvider.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,34 @@
    @SuppressLint("RestrictedApi")
    @Composable
    fun <T : ViewModel> ComposableScopeViewModelProvider(
    key: String,
    viewModelFactory: @Composable (key: String) -> T,
    content: @Composable (viewModel: T) -> Unit,
    ) {
    val activity = LocalActivity.current ?: return
    val viewModelStoreOwner = LocalViewModelStoreOwner.current ?: return
    val lifecycleOwner = LocalLifecycleOwner.current

    DisposableEffect(lifecycleOwner) {
    var isConfigurationChanging = false
    val observer = LifecycleEventObserver { _, event ->
    if (event == Lifecycle.Event.ON_DESTROY) {
    isConfigurationChanging = activity.isChangingConfigurations
    }
    }
    lifecycleOwner.lifecycle.addObserver(observer)
    onDispose {
    lifecycleOwner.lifecycle.removeObserver(observer)
    if (!isConfigurationChanging) {
    // Replace the ViewModel with an empty one to make the ViewModel clear
    viewModelStoreOwner.viewModelStore.put(
    key = key,
    viewModel = object : ViewModel() {
    override fun onCleared() = Unit
    }
    )
    }
    }
    }
    content(viewModelFactory(key))
    }
    48 changes: 48 additions & 0 deletions NoSavedStateViewModel.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,48 @@
    /*
    * Composable has gone by UI logic: ViewModel and data are cleared
    * Configuration changes: ViewModel and data still exist.
    * Application process recreation: Clear ViewModel and data.
    */

    // SectionViewModel.kt
    class SectionViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    val uiState: StateFlow<String?> =
    savedStateHandle.getStateFlow<String?>(
    key = "uiState",
    initialValue = null,
    )

    fun updateValue() {
    val newValue = UUID.randomUUID().toString()
    savedStateHandle["uiState"] = newValue
    }

    override fun onCleared() {
    super.onCleared()
    // Clean up is requires for any saved state value
    savedStateHandle.keys().forEach { key ->
    savedStateHandle.remove<Any>(key)
    }
    }
    }

    // SampleScreen.kt
    @Composable
    fun SampleScreen() {
    var showSection by remember { mutableStateOf<Boolean>(false)

    /* ... */
    if (showSection) {
    ComposableScopeViewModelProvider(
    key = "section",
    factory = { key -> viewModel<SectionViewModel>(key = key) }
    // Koin library
    // factory = { key -> koinViewModel<SectionViewModel>(key = key) },
    ) { viewModel ->
    StatefulSection()
    }
    }
    }

    @Composable
    fun StatefulSection(viewModel: SectionViewModel) { /* .. */ }
    37 changes: 37 additions & 0 deletions WithSavedStateViewModel.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,37 @@
    /*
    * Composable has gone by UI logic: ViewModel and data are cleared.
    * Configuration changes: ViewModel and data still exist.
    * Application process recreation: ViewModel cleared, but data still exists.
    */

    // SectionViewModel.kt
    class SectionViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
    private val _uiState = MutableStateFlow<String?>(null)
    val uiState: StateFlow<String?> = _uiState

    fun updateValue() {
    val newValue = UUID.randomUUID().toString()
    _uiState2.update { newValue }
    }
    }

    // SampleScreen.kt
    @Composable
    fun SampleScreen() {
    var showSection by remember { mutableStateOf<Boolean>(false)

    /* ... */
    if (showSection) {
    ComposableScopeViewModelProvider(
    key = "section",
    factory = { key -> viewModel<SectionViewModel>(key = key) }
    // Koin library
    // factory = { key -> koinViewModel<SectionViewModel>(key = key) },
    ) { viewModel ->
    StatefulSection()
    }
    }
    }

    @Composable
    fun StatefulSection(viewModel: SectionViewModel) { /* .. */ }