Compose Multiplatform library for remembering state persistently based on Multiplatform Settings. It's a persistent version of remember { mutableStateOf(x) }
.
Demo: https://burnoo.github.io/compose-remember-setting/
The library is distributed through Maven Central. To use it, add the following dependency to your module build.gradle.kts
:
dependencies {
implementation("dev.burnoo:compose-remember-setting:1.0.1")
}
To store mutable state in @Composable
when the app is running, you usually use remember { mutableStateOf(x) }
. This library provides the same functionality but supports data persistence, saving, and restoring data using Multiplatform Settings.
val count: MutableState<Int> = rememberIntSetting(key = "intKey", defaultValue = 0)
var text: String? by rememberStringSettingOrNull(key = "stringKey")
val (checked, setChecked) = rememberBooleanSetting(key = "booleanKey", defaultValue = false)
Full example can be found here.
You can configure the library using the local composition mechanism to pass ComposeRememberSettingConfig
, which stores ObservableSettings
and CoroutineDispatcher
for use by the children.
@Composable
fun WithSettings(content: @Composable () -> Unit)() {
CompositionLocalProvider(
LocalComposeRememberSettingConfig provides ComposeRememberSettingConfig(
observableSettings = CustomSettings().makeObservable(),
flowSettingsDispatcher = Dispatchers.Unconfined
),
content = content
)
}
Code
@Composable
fun ComposeRememberSettingPreview(
vararg keyValues: Pair<String, Any>,
content: @Composable () -> Unit
) {
CompositionLocalProvider(
LocalComposeRememberSettingConfig provides ComposeRememberSettingConfig(
mutableMap = mutableMapOf(*keyValues),
),
content = content,
)
}
@Preview
@Composable
fun CounterPreview() {
ComposeRememberSettingPreview("intKey" to 21) {
var counter: Int by rememberIntSetting(key = "intKey", defaultValue = 0)
Button(onClick = { counter++ }) { Text(counter.toString()) }
}
}
Code
@Composable
private fun WithTestComposeSettings(
observableSettings: ObservableSettings,
content: @Composable () -> Unit,
) {
CompositionLocalProvider(
LocalComposeRememberSettingConfig provides ComposeRememberSettingConfig(
observableSettings = observableSettings,
flowSettingsDispatcher = Dispatchers.Unconfined,
),
content = content,
)
}
@OptIn(ExperimentalTestApi::class)
class ComposeTest {
private val settings = MapSettings(mutableMapOf("intKey" to 21))
@Test
fun counterTest() = runComposeUiTest {
setContent {
WithTestComposeSettings(settings) {
var counter: Int by rememberIntSetting(key = "intKey", defaultValue = 0)
}
}
}
}
String
-rememberStringSetting
/rememberStringSettingOrNull
Int
-rememberIntSetting
,rememberIntSettingOrNull
Long
-rememberLongSetting
,rememberLongSettingOrNull
Boolean
-rememberBooleanSetting
,rememberBooleanSettingOrNull
Float
-rememberFloatSetting
,rememberFloatSettingOrNull
Double
-rememberDoubleSetting
,rememberDoubleSettingOrNull
You might ask why someone would need to use this library. The data layer should not be used directly on the UI and should be handled in the repository.
This is valid for any more complicated logic. However, when there is a need to store a very simple state of the UI persistently, the library becomes very handy, reducing coupling.
Example: Imagine a user wants the checkbox state to be remembered after the app is restarted. With this library, the code is very simple: var checked by rememberBooleanSetting(key = "booleanKey", defaultValue = false)
.
Implementing this in a clean codish manner is much more complicated. We would need a ViewModel
(with passing lambda and state through all the composables), a LayoutStateRepository
(assuming we don't want to use Settings
in the ViewModel
either, as we use a Repository
abstraction over data sources), a LayoutStateLocalDataSource
, and maybe even GetCheckboxStateUseCase
and SetCheckboxStateUseCase
if the ViewModel
doesn’t depend on :data directly.
Copyright 2024 Bruno Wieczorek
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.