Settings Screen in Compose — Switch, Slider & Selection Dialogs
Create professional settings screens in Jetpack Compose with Switch, Slider, dialogs, and persistent DataStore integration.
SwitchSetting Component
Build reusable switch items with ListItem:
@Composable
fun SwitchSetting(
title: String,
description: String? = null,
isChecked: Boolean,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier
) {
ListItem(
modifier = modifier.clickable { onCheckedChange(!isChecked) },
headlineContent = { Text(title) },
supportingContent = description?.let { { Text(it) } },
trailingContent = {
Switch(
checked = isChecked,
onCheckedChange = onCheckedChange
)
}
)
}
@Composable
fun SettingsScreenWithSwitches() {
var darkMode by remember { mutableStateOf(false) }
var notifications by remember { mutableStateOf(true) }
LazyColumn {
item {
SwitchSetting(
title = "Dark Mode",
description = "Enable dark theme",
isChecked = darkMode,
onCheckedChange = { darkMode = it }
)
}
item {
SwitchSetting(
title = "Notifications",
description = "Receive push notifications",
isChecked = notifications,
onCheckedChange = { notifications = it }
)
}
}
}
SliderSetting with Value Label
Display numeric settings with visual feedback:
@Composable
fun SliderSetting(
title: String,
value: Float,
onValueChange: (Float) -> Unit,
valueRange: ClosedFloatingPointRange<Float> = 0f..100f,
modifier: Modifier = Modifier
) {
Column(modifier = modifier.padding(16.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(title, style = MaterialTheme.typography.titleMedium)
Text(
value.toInt().toString(),
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary
)
}
Slider(
value = value,
onValueChange = onValueChange,
valueRange = valueRange,
modifier = Modifier.fillMaxWidth()
)
}
}
@Composable
fun VolumeSliderExample() {
var volume by remember { mutableFloatStateOf(70f) }
SliderSetting(
title = "Volume",
value = volume,
onValueChange = { volume = it }
)
}
ClickableSetting for Navigation
Clickable list items for navigation to detail screens:
@Composable
fun ClickableSetting(
title: String,
description: String? = null,
icon: (@Composable () -> Unit)? = null,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
ListItem(
modifier = modifier.clickable(onClick = onClick),
headlineContent = { Text(title) },
supportingContent = description?.let { { Text(it) } },
leadingContent = icon,
trailingContent = {
Icon(Icons.AutoMirrored.Filled.ChevronRight, contentDescription = null)
}
)
}
@Composable
fun SettingsNavigation() {
ClickableSetting(
title = "Account",
description = "Manage your account settings",
icon = { Icon(Icons.Filled.AccountCircle, contentDescription = null) },
onClick = { /* Navigate to account */ }
)
}
Section Headers
Group settings with colored section headers:
@Composable
fun SettingsSection(
title: String,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
Column(modifier = modifier) {
Text(
title,
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.primaryContainer)
.padding(16.dp),
color = MaterialTheme.colorScheme.onPrimaryContainer,
style = MaterialTheme.typography.labelLarge
)
content()
}
}
@Composable
fun CategorizedSettings() {
LazyColumn {
item {
SettingsSection(title = "Display") {
SwitchSetting(
title = "Dark Mode",
isChecked = true,
onCheckedChange = {}
)
}
}
}
}
Language Selection Dialog with RadioButton
Implement choice dialogs for preference selection:
@Composable
fun LanguageDialog(
isOpen: Boolean,
currentLanguage: String,
onDismiss: () -> Unit,
onLanguageSelected: (String) -> Unit
) {
val languages = listOf("English", "Spanish", "French", "Japanese")
if (isOpen) {
AlertDialog(
onDismissRequest = onDismiss,
title = { Text("Select Language") },
text = {
LazyColumn {
items(languages) { language ->
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onLanguageSelected(language) }
.padding(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
selected = language == currentLanguage,
onClick = { onLanguageSelected(language) }
)
Text(language, modifier = Modifier.padding(start = 8.dp))
}
}
}
},
confirmButton = {
TextButton(onClick = onDismiss) {
Text("Done")
}
}
)
}
}
DataStore Integration for Persistence
Persist settings across app restarts:
// Create DataStore
val Context.settingsDataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
object SettingsKeys {
val DARK_MODE = booleanPreferencesKey("dark_mode")
val VOLUME = floatPreferencesKey("volume")
val LANGUAGE = stringPreferencesKey("language")
}
@Composable
fun PersistentSettingsScreen(context: Context) {
val settingsDataStore = context.settingsDataStore
val darkMode = settingsDataStore.data
.map { it[SettingsKeys.DARK_MODE] ?: false }
.collectAsState(initial = false)
val coroutineScope = rememberCoroutineScope()
SwitchSetting(
title = "Dark Mode",
isChecked = darkMode.value,
onCheckedChange = { newValue ->
coroutineScope.launch {
settingsDataStore.edit { preferences ->
preferences[SettingsKeys.DARK_MODE] = newValue
}
}
}
)
}
Key Takeaways
- ListItem with Switch for boolean toggles
- Slider for numeric range settings with visual feedback
- Clickable ListItems for navigation to detail screens
- Section headers to organize related settings
- RadioButton dialogs for single-selection preferences
- DataStore to persist user settings across sessions
Combine these patterns to build complete, user-friendly settings experiences!
8 Android app templates: Gumroad
Top comments (0)