I often use settings in my apps to store single values outside a database. It’s rare that I write an app without using settings in one way or another.

This tutorial shows platform-independent settings. We’ll create a single codebase that works on both Apple and Android platforms.

Here is a simple example. First, we declare and instantiate our settings. Then we save a value for a specific key. In the third line, we retrieve the setting. It’s as straightforward as that.

val settings: Settings = Settings()
settings.putInt("key", 3)
settings["key"] = 3

What does that look like in the context of a Kotlin Multiplatform application?

In the following example, we aim to retain the myBool value whenever the user toggles the Switch. The value should persist even after restarting the app.

import androidx.compose.material.MaterialTheme
import androidx.compose.material.Switch
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import com.russhwolf.settings.Settings
 
@Composable
fun App() {
 MaterialTheme {
  var myBool by remember { mutableStateOf(false) }
 
  Switch(
   checked = myBool,
   onCheckedChange = {
    myBool = it
   }
  )
 }
}

Let’s start by adding the dependency:

implementation("com.russhwolf:multiplatform-settings-no-arg:1.1.1")

Below is our application that persists the Boolean value. To test, restart the application after changing the value.

import androidx.compose.material.MaterialTheme
import androidx.compose.material.Switch
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import com.russhwolf.settings.Settings
 
@Composable
fun App() {
 MaterialTheme {
  val settings: Settings = remember { Settings() }
 
  var myBool by remember { mutableStateOf(settings.getBoolean("bool", false)) }
 
  Switch(
   checked = myBool,
   onCheckedChange = {
    settings.putBoolean("bool", it)
    myBool = settings.getBoolean("bool", false)
   }
  )
 }
}

I prefer having a single source of truth. Currently we have two: the settings and myBool.

I like using flows, particularly for exposing settings to code that should automatically update whenever a setting changes.

Let’s apply that concept to our example application.

First, add this dependency:

implementation("com.russhwolf:multiplatform-settings-coroutines:1.1.1")

The most significant change is turning regular Settings into FlowSettings.

import androidx.compose.material.MaterialTheme
import androidx.compose.material.Switch
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import com.russhwolf.settings.ExperimentalSettingsApi
import com.russhwolf.settings.ObservableSettings
import com.russhwolf.settings.Settings
import com.russhwolf.settings.coroutines.toFlowSettings
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
 
@OptIn(ExperimentalSettingsApi::class, DelicateCoroutinesApi::class)
@Composable
fun App() {
 MaterialTheme {
  val flowSettings = remember { (Settings() as ObservableSettings).toFlowSettings() }
 
  val boolState by flowSettings
  .getBooleanFlow(key = "bool", defaultValue = false)
  .collectAsState(initial = false)
 
  Switch(
   checked = boolState,
   onCheckedChange = { checked ->
    GlobalScope.launch {
     flowSettings.putBoolean("bool", checked)
    }
   }
  )
 }
}

This brief guide should quickly familiarize you with using Settings in your KMP project. I hope you find it useful and enjoyable!

If you have any further questions, need specific code examples, or require additional assistance, please leave a comment or send me a message.

Thank you for reading!

Resources