Skip to content

ComposePreferences#

Open in GitHub

Latest Release Last Update License

Stars Forks

This library offers you preference screens for compose including the ability of endless nesting subscreens as well as simple integration of custom preferences.

📷 Screenshots#

Screenshots
Demo Demo Demo
Demo Demo Demo
Demo Demo Demo
Demo

Features#

  • offers a simple PreferenceScreen composable
  • does offer all common used preferences
  • manages nesting, visibilities and dependencies for you
  • can be easily extended

All features are splitted into separate modules, just include the modules you want to use!

🔗 Dependencies#

Compose

Dependency Version Infos
Compose BOM 2024.04.00 Mapping
Material3 1.2.1

Library

Module Dependency Version
core -
Screens
screen-bool -
screen-button -
screen-input ComposeDialogs 1.0.8
screen-color ComposeDialogs 1.0.8
screen-date ComposeDialogs 1.0.8
screen-time ComposeDialogs 1.0.8
screen-list ComposeDialogs 1.0.8
screen-number ComposeDialogs 1.0.8
Extensions
extension-kotpreferences KotPreferences 0.5.1

Setup Gradle#

This library is distributed via JitPack.io.

1/2: Add jitpack to your project's build.gradle
repositories {
    maven { url "https://jitpack.io" }
}
2/2: Add dependencies to your module's build.gradle
// use the latest version of the library
val composepreferences = "<LATEST-VERSION>" 

// include necessary modules

// core module
implementation("com.github.MFlisar.ComposePreferences:core:$composepreferences")

// screen module
implementation("com.github.MFlisar.ComposePreferences:screen-bool:$composepreferences")
implementation("com.github.MFlisar.ComposePreferences:screen-button:$composepreferences")
implementation("com.github.MFlisar.ComposePreferences:screen-input:$composepreferences")
implementation("com.github.MFlisar.ComposePreferences:screen-color:$composepreferences")
implementation("com.github.MFlisar.ComposePreferences:screen-date:$composepreferences")
implementation("com.github.MFlisar.ComposePreferences:screen-time:$composepreferences")
implementation("com.github.MFlisar.ComposePreferences:screen-list:$composepreferences")
implementation("com.github.MFlisar.ComposePreferences:screen-number:$composepreferences")

// extension module
implementation("com.github.MFlisar.ComposePreferences:extension-kotpreferences:$composepreferences")

⌨ Usage#

It works as simple as following:

// Preferences must be wrapped in a screen
// => this allows to manage internal hierarchy and screen nesting and everything is managed automatically
// => this also enables internal scrolling
PreferenceScreen(
    // optional parameters to customise this screen
    settings =  PreferenceSettingsDefaults.settings(),
    scrollable = true
) {
    // Preferences at root level
    PreferenceInfo(
        title = {  Text("Info 1") }
    )
    PreferenceBool(
        style = PreferenceBool.Style.Switch,
        value = <value>,
        onValueChange = {
            // update value here
        },
        title = { Text("Bool") }
        )

    // Sub Preference - all nested preferences will show if you click the sub preference (and all preferences from other levels will be hidden automatically)
    // + back press + state saving will be handled automatically
    PreferenceSubScreen(
        title = { Text("Menu") }
    ) {
        // sub preferences must be placed here
        // you can even nest another PreferenceSubScreen here - any nesting depth is supported!
    }

    // IMPORTANT:
    // don't place any non preference composables here, they won't be correctly shown/hidden and managed by the preference screen because they don't hold any hierarchical data!
    // also gray out and enabled states won't work
    // if you want to place some custom content wrap it inside a `BasePreference` (if you want the default title/subtitle/icon/content layout) 
    // or inside a `BasePreferenceContainer` if you want to place plain content

    // Custom 1 - default button inside the content area wrapped as preference => this will work correctly even if used in sub preferences and it will automatically support enabled/disabling
    BasePreference(
        title = { Text("A custom preference") },
        subtitle = { Text("Showing a button") },
        icon = { Icon(Icons.Default.Android, null) }
    ) {
        Button(onClick = {
            // ...
        }) {
            Icon(Icons.Default.Android, null)
        }
    }

    // Custom 2 - completely free content => this will also work correctly even if used in sub preferences and it will automatically support enabled/disabling
    // but it allows you to wrap ANY composable inside it
    BasePreferenceContainer(
        modifier = Modifier.padding(16.dp),
        preferenceStyle = PreferenceStyleDefaults.item()
    ) { modifier ->
        Button(
            onClick = {
                // ...
        }) {
            Text("Button")
        }
    }
}

🧬 Demo#

A full demo is included inside the demo module, it shows nearly every usage with working examples.

Modules and Extensions#

Info Preference
Preview Module
Preview core
PreferenceInfo.kt
fun PreferenceScope.PreferenceInfo(
    // Special
    onLongClick: (() -> Unit)? = null,
    ignoreMinItemHeight: Boolean = false,
    alignment: Alignment.Vertical = Alignment.CenterVertically,
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {
Divider Preference
Preview Module
Preview core
PreferenceDivider.kt
fun PreferenceScope.PreferenceDivider(
    // Special
    // Base Preference
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled
) {
Section Header Preference
Preview Module
Preview core
PreferenceSectionHeader.kt
fun PreferenceScope.PreferenceSectionHeader(
    // Special
    // Base Preference
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    title: @Composable () -> Unit,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = PreferenceStyleDefaults.header()
) {
Bool Preference
Preview Module
Preview Preview bool
PreferenceBool.kt
fun PreferenceScope.PreferenceBool(
    style: PreferenceBool.Style = PreferenceBool.Style.Switch,
    // Special
    value: Boolean,
    onValueChange: (selected: Boolean) -> Unit,
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {
Button Preference
Preview Module
Preview button
PreferenceButton.kt
fun PreferenceScope.PreferenceButton(
    // Special
    onClick: (() -> Unit),
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {
Color Preference
Preview Module
Preview Preview color
PreferenceColor.kt
fun PreferenceScope.PreferenceColor(
    // Special
    value: Color,
    onValueChange: (value: Color) -> Unit,
    alphaSupported: Boolean = true,
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {
Date Preference
Preview Module
Preview date
PreferenceDate.kt
fun PreferenceScope.PreferenceDate(
    // Special
    value: LocalDate,
    onValueChange: (date: LocalDate) -> Unit,
    firstDayOfWeek: DayOfWeek = DayOfWeek.MONDAY,
    formatter: (date: LocalDate) -> String = {
        it.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG))
    },
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {
Input Preference
Preview Module
Preview input
Preview Preview input
PreferenceInputText.kt
fun PreferenceScope.PreferenceInputText(
    // Special
    value: String,
    onValueChange: (value: String) -> Unit,
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {
PreferenceInputNumber.kt
fun <T : Number> PreferenceScope.PreferenceInputNumber(
    // Special
    value: T,
    onValueChange: (value: T) -> Unit,
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {
Input List
Preview Module
Preview Preview list
Preview list

This preference does offer 2 different options, one that allows you to only select a single item and one that allows to select mutliple items.

PreferenceList.kt
fun <T> PreferenceScope.PreferenceList(
    style: PreferenceList.Style = PreferenceList.Style.Dialog,
    // Special
    value: T,
    onValueChange: (value: T) -> Unit,
    items: List<T>,
    itemTextProvider: @Composable (item: T) -> String,
    itemIconProvider: (@Composable (item: T) -> Unit)? = null,
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {
PreferenceListMulti.kt
fun <T> PreferenceScope.PreferenceListMulti(
    // Special
    value: List<T>,
    onValueChange: (value: List<T>) -> Unit,
    items: List<T>,
    itemTextProvider: @Composable (item: T) -> String,
    itemIconProvider: (@Composable (item: T) -> Unit)? = null,
    formatter: @Composable (selected: List<T>) -> String = { selected ->
        selected.map {
            itemTextProvider(it)
        }.joinToString(";")
    },
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {
Number Preference
Preview Module
Preview number
PreferenceNumber.kt
fun <T : Number> PreferenceScope.PreferenceNumber(
    style: PreferenceNumber.Style = PreferenceNumber.Style.Picker,
    // Special
    value: T,
    onValueChange: (value: T) -> Unit,
    min: T,
    max: T,
    stepSize: T,
    formatter: (value: T) -> String = { it.toString() },
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {
Time Preference
Preview Module
Preview time
PreferenceTime.kt
fun PreferenceScope.PreferenceTime(
    // Special
    value: LocalTime,
    onValueChange: (value: LocalTime) -> Unit,
    is24Hours: Boolean = DateFormat.is24HourFormat(LocalContext.current),
    formatter: (time: LocalTime) -> String = getDefaultTimeFormatter(is24Hours),
    // Base Preference
    title: @Composable () -> Unit,
    enabled: Dependency = Dependency.Enabled,
    visible: Dependency = Dependency.Enabled,
    subtitle: @Composable (() -> Unit)? = null,
    icon: (@Composable () -> Unit)? = null,
    preferenceStyle: PreferenceStyle = LocalPreferenceSettings.current.itemStyle
) {