
Simplifies ViewModel state persistence across process death and configuration changes, integrating StateFlow with SavedStateHandle. Offers automatic state saving/restoring, selective persistence, and reduces boilerplate code.
Also see a Demo App Using Reanimator
Effortless StateFlow persistence for Android and Kotlin Multiplatform ViewModels using SavedStateHandle.
Reanimator simplifies managing ViewModel state persistence across process death (Android) or configuration changes by seamlessly integrating Kotlin's StateFlow with SavedStateHandle. It offers automatic state saving/restoring with selective persistence for transient properties, significantly reducing boilerplate code.
StateFlow<T> state.SavedStateHandle, reducing configuration.commonMain code for shared ViewModel logic across Android and other platforms supporting SavedStateHandle (via KMP ViewModel libraries).kotlinx.serialization for robust state serialization (JSON by default).StateFlow in a single line.Handling process death or configuration changes often involves manually interacting with SavedStateHandle:
This leads to repetitive, error-prone code that clutters your ViewModel.
Reanimator provides the getMutableStateFlow extension function for SavedStateHandle.
// In your ViewModel (Android or KMP commonMain)
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
// Define which properties are transient
private val transientProps = listOf("isLoading", "error")
// Declare your state flow - Reanimator handles the rest!
private val _uiState by savedStateHandle.getMutableStateFlow(
defaultValue = MyUiState(), // Initial/default state
coroutineScope = viewModelScope, // Scope for saving changes
transientProperties = transientProps // What NOT to save
// key = "custom_state_key" // Optional: custom key
)
val uiState: StateFlow<MyUiState> = _uiState.asStateFlow()
// ... rest of your ViewModel logic ...
fun updateData(newData: List<String>) {
// Just update the state - Reanimator saves persistent parts automatically
_uiState.update { it.copy(data = newData, isLoading = false) }
}
}
// Your state class (must be @Serializable)
@Serializable
data class MyUiState(
// Persistent
val data: List<String> = emptyList(),
val selectedItem: String? = null,
// Transient
val isLoading: Boolean = false,
val error: String? = null
)With Reanimator, you simply define your state, mark it @Serializable, identify transient properties, and declare the StateFlow. Reanimator takes care of saving persistent data to SavedStateHandle whenever the state updates and restoring it correctly (while resetting transient fields) when the ViewModel is recreated.
Add the Reanimator dependency to your build.gradle.kts (or build.gradle) file.
For Kotlin Multiplatform (commonMain):
kotlin {
sourceSets {
commonMain.dependencies {
// Navigation Library enables SavedStateHandle inside commonMain
implementation(libs.jetbrains.compose.navigation)
// Serialization library to handle complex data types for SavedStateHandle
implementation(libs.kotlinx.serialization.json)
implementation("eu.anifantakis:reanimator:1.0.5")
}
}
}For Android-only:
dependencies {
// Navigation
implementation(libs.androidx.navigation.compose)
// Serialization library to handle complex data types for SavedStateHandle
implementation(libs.kotlinx.serialization.json)
implementation("eu.anifantakis:reanimator-android:1.0.5")
}(Remember to have kotlinx.serialization configured in your project.)
SavedStateHandle: Ensure your ViewModel receives an instance of SavedStateHandle. This is standard practice in Android and supported by KMP ViewModel libraries.@Serializable data class for your UI state.by savedStateHandle.getMutableStateFlow(...) property delegate.fun <reified T : Any> SavedStateHandle.getMutableStateFlow(
defaultValue: T, // Required: The initial state and source for transient defaults.
coroutineScope: CoroutineScope, // Required: Scope to collect state changes for saving (e.g., viewModelScope).
key: String? = null, // Optional: Custom key for SavedStateHandle. Defaults to the property name.
transientProperties: List<String> = emptyList(), // Optional: List of property names to exclude from saving.
json: Json = Json { ignoreUnknownKeys = true } // Optional: Custom kotlinx.serialization Json instance.
): PropertyDelegateProvider<Any, ReadOnlyProperty<Any, MutableStateFlow<T>>>defaultValue: The state instance used when no saved state exists, and crucially, the source for default values of transientProperties upon restoration.coroutineScope: The scope (usually viewModelScope) where the background collection runs to automatically save state changes.key: The string key used to store the state in SavedStateHandle. If null, the name of the delegated property (e.g., _uiState) is used.transientProperties: A list of property names (as strings) within your state class T that should be ignored during saving and reset to their defaultValue values during restoration.json: Allows providing a custom configured kotlinx.serialization.json.Json instance if needed (e.g., for specific encoding strategies). Defaults to a lenient instance that ignores unknown keys.Reanimator observes the MutableStateFlow you create. On each update:
kotlinx.serialization.transientProperties are defined and the state serializes to a JSON object, it removes the fields corresponding to those transient properties from the JSON.SavedStateHandle under the specified or inferred key.When the ViewModel is recreated:
SavedStateHandle using the key.transientProperties were defined, it intelligently merges the deserialized persistent data with the default values for transient properties taken from the defaultValue instance.MutableStateFlow.defaultValue is used.Also see a Demo App Using Reanimator
Effortless StateFlow persistence for Android and Kotlin Multiplatform ViewModels using SavedStateHandle.
Reanimator simplifies managing ViewModel state persistence across process death (Android) or configuration changes by seamlessly integrating Kotlin's StateFlow with SavedStateHandle. It offers automatic state saving/restoring with selective persistence for transient properties, significantly reducing boilerplate code.
StateFlow<T> state.SavedStateHandle, reducing configuration.commonMain code for shared ViewModel logic across Android and other platforms supporting SavedStateHandle (via KMP ViewModel libraries).kotlinx.serialization for robust state serialization (JSON by default).StateFlow in a single line.Handling process death or configuration changes often involves manually interacting with SavedStateHandle:
This leads to repetitive, error-prone code that clutters your ViewModel.
Reanimator provides the getMutableStateFlow extension function for SavedStateHandle.
// In your ViewModel (Android or KMP commonMain)
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
// Define which properties are transient
private val transientProps = listOf("isLoading", "error")
// Declare your state flow - Reanimator handles the rest!
private val _uiState by savedStateHandle.getMutableStateFlow(
defaultValue = MyUiState(), // Initial/default state
coroutineScope = viewModelScope, // Scope for saving changes
transientProperties = transientProps // What NOT to save
// key = "custom_state_key" // Optional: custom key
)
val uiState: StateFlow<MyUiState> = _uiState.asStateFlow()
// ... rest of your ViewModel logic ...
fun updateData(newData: List<String>) {
// Just update the state - Reanimator saves persistent parts automatically
_uiState.update { it.copy(data = newData, isLoading = false) }
}
}
// Your state class (must be @Serializable)
@Serializable
data class MyUiState(
// Persistent
val data: List<String> = emptyList(),
val selectedItem: String? = null,
// Transient
val isLoading: Boolean = false,
val error: String? = null
)With Reanimator, you simply define your state, mark it @Serializable, identify transient properties, and declare the StateFlow. Reanimator takes care of saving persistent data to SavedStateHandle whenever the state updates and restoring it correctly (while resetting transient fields) when the ViewModel is recreated.
Add the Reanimator dependency to your build.gradle.kts (or build.gradle) file.
For Kotlin Multiplatform (commonMain):
kotlin {
sourceSets {
commonMain.dependencies {
// Navigation Library enables SavedStateHandle inside commonMain
implementation(libs.jetbrains.compose.navigation)
// Serialization library to handle complex data types for SavedStateHandle
implementation(libs.kotlinx.serialization.json)
implementation("eu.anifantakis:reanimator:1.0.5")
}
}
}For Android-only:
dependencies {
// Navigation
implementation(libs.androidx.navigation.compose)
// Serialization library to handle complex data types for SavedStateHandle
implementation(libs.kotlinx.serialization.json)
implementation("eu.anifantakis:reanimator-android:1.0.5")
}(Remember to have kotlinx.serialization configured in your project.)
SavedStateHandle: Ensure your ViewModel receives an instance of SavedStateHandle. This is standard practice in Android and supported by KMP ViewModel libraries.@Serializable data class for your UI state.by savedStateHandle.getMutableStateFlow(...) property delegate.fun <reified T : Any> SavedStateHandle.getMutableStateFlow(
defaultValue: T, // Required: The initial state and source for transient defaults.
coroutineScope: CoroutineScope, // Required: Scope to collect state changes for saving (e.g., viewModelScope).
key: String? = null, // Optional: Custom key for SavedStateHandle. Defaults to the property name.
transientProperties: List<String> = emptyList(), // Optional: List of property names to exclude from saving.
json: Json = Json { ignoreUnknownKeys = true } // Optional: Custom kotlinx.serialization Json instance.
): PropertyDelegateProvider<Any, ReadOnlyProperty<Any, MutableStateFlow<T>>>defaultValue: The state instance used when no saved state exists, and crucially, the source for default values of transientProperties upon restoration.coroutineScope: The scope (usually viewModelScope) where the background collection runs to automatically save state changes.key: The string key used to store the state in SavedStateHandle. If null, the name of the delegated property (e.g., _uiState) is used.transientProperties: A list of property names (as strings) within your state class T that should be ignored during saving and reset to their defaultValue values during restoration.json: Allows providing a custom configured kotlinx.serialization.json.Json instance if needed (e.g., for specific encoding strategies). Defaults to a lenient instance that ignores unknown keys.Reanimator observes the MutableStateFlow you create. On each update:
kotlinx.serialization.transientProperties are defined and the state serializes to a JSON object, it removes the fields corresponding to those transient properties from the JSON.SavedStateHandle under the specified or inferred key.When the ViewModel is recreated:
SavedStateHandle using the key.transientProperties were defined, it intelligently merges the deserialized persistent data with the default values for transient properties taken from the defaultValue instance.MutableStateFlow.defaultValue is used.