MaterialThemePrefs
1.0.0indexedFacilitates easy switching between Dark/Light Material themes using composable functions, providing theme preferences items, dialogs, and persistent storage across multiple platforms.
Facilitates easy switching between Dark/Light Material themes using composable functions, providing theme preferences items, dialogs, and persistent storage across multiple platforms.
Kotlin Multiplatform library for easy switching Dark/Light Material themes on Compose. Supported platforms:


Call composable functions to wrap your app, show theme preference items, and handle dialog state manually.
Show dialog directly:
Use ThemeAlertDialog to show a dialog with theme preferences:
@Composable
fun App() = PreferableMaterialTheme {
var showDialog by remember { mutableStateOf(false) }
SettingsScaffold(onThemeClick = { showDialog = true }) {
Column {
ThemePreferencesCategory() // subtitle
ThemePreferenceItem(onClick = { showDialog = true }) // menu item
}
}
if (showDialog) {
ThemeAlertDialog(dismissDialog = { showDialog = false })
}
}
With navigation frameworks:
Use ThemeDialogContent as the content of a dialog destination:
@Composable
fun App() = PreferableMaterialTheme {
val navController = rememberNavController()
NavHost(navController = navController) {
composable<AppNavGraph.Settings> {
SettingsBody(onThemeClick = { navController.navigate(route = AppNavGraph.ThemeDialog) })
}
dialog<AppNavGraph.ThemeDialog> {
ThemeDialogContent(dismissDialog = navController::popBackStack)
}
}
}
Check the sample app for using with Material Design versions 2 & 3.
The NoteDelight app is a real example.
The latest release is available on Maven Central.
repositories {
mavenCentral()
}
commonMain {
dependencies {
implementation("io.github.softartdev:theme-material:$latestVersion") // Material Design 2
implementation("io.github.softartdev:theme-material3:$latestVersion") // Material Design 3
implementation("io.github.softartdev:theme-prefs:$latestVersion") // optional, if you need only preferences
}
}
Used compose-multiplatform-resources library for many languages (currently Russian and English are supported).
Persisting preferences is implemented using SharedPreferences on Android, and Java Preference API on JVM Desktop.
Also used composition local for access from theme-scoped as an implicit way:
val themePrefs: ThemePrefs = LocalThemePrefs.current
// common:
expect var themeEnum: ThemeEnum
// android:
private val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
actual var themeEnum: ThemeEnum
get() = preferences.getInt(THEME_KEY, ThemeEnum.SystemDefault.ordinal).let(ThemeEnum.values()::get)
set(value) = preferences.edit().putInt(THEME_KEY, value.ordinal).apply()
// desktop java:
private var preferences: Preferences = Preferences.userNodeForPackage(ThemeEnum::class.java)
actual var themeEnum: ThemeEnum
get() = preferences.getInt(THEME_KEY, ThemeEnum.SystemDefault.ordinal).let(ThemeEnum.values()::get)
set(value) = preferences.putInt(THEME_KEY, value.ordinal)
// ios:
private val preferences: NSUserDefaults = NSUserDefaults.standardUserDefaults
actual var themeEnum: ThemeEnum
get() = preferences.integerForKey(THEME_KEY).let(ThemeEnum.values()::get)
set(value) = preferences.setInteger(value.ordinal, THEME_KEY)
// wasm js:
private val storage: Storage = window.localStorage
actual var themeEnum: ThemeEnum
get() = storage.getItem(THEME_KEY)?.toIntOrNull()?.let(ThemeEnum.values()::get) ?: ThemeEnum.SystemDefault
set(value) = storage.setItem(THEME_KEY, value.ordinal.toString())
Surfaced from shared tags and platforms — no rankings paid for.