/ComposeDynamicTheme

Provides Dynamic Theme for Jetpack Compose

Primary LanguageKotlinApache License 2.0Apache-2.0

Title-2

API API API License API


🚀 Introduced in Android Dev Notes Link

✅ Next Roadmap

  • support change color on status bar

What's Dynamic Theme

Dynamic Theme is a Material Design-based Theme Management System for Android Jetpack Compose. Up until now, changing the theme on Android has been a very difficult task. Dynamic Theme was created to make Android's theme management easy. Theming can be applied by simply adding 'ProvidesTheme' to the top-level declaration in Jetpack Compose.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            DynamicThemeService.get().ProvidesTheme {
                // Add Compose Functions
            }
        }
    }
}

Since this theme management system is based on Material Designs, knowledge of the Material Design System is required to use this library.


Preview



animated



Downloads

Maven Central

Material2 : Maven Central
Material3 : Maven Central

Getting Started

Add the mavenCentral() on project level(root level) build.gradle file:

allprojects {
    repositories {
        mavenCentral()
    }
}

Add dependency on module level build.gradle file:

build.gradle

dependencies {
    // Dynamic Theme support for Material2
    implementation "io.github.seyoungcho2:dynamic-theme-compose:0.0.3"
    
    // Dynamic Theme support for Material3
    implementation "io.github.seyoungcho2:dynamic-theme-compose-material3:0.0.3"
}

If using build.gradle.kts

build.gradle.kts

dependencies {
    // Dynamic Theme support for Material2
    implementation("io.github.seyoungcho2:dynamic-theme-compose:0.0.3")
        
    // Dynamic Theme support for Material3
    implementation("io.github.seyoungcho2:dynamic-theme-compose-material3:0.0.3")
}



Features

Table of Contents

ThemeModelKey

The ThemeModelKey is used to register the ThemeModel and access the registered ThemeModel.

  • ThemeModelKey can be declared by using the function on ThemeModelKey
val THEME_MODEL_KEY_BLUE = ThemeModelKey.of("Blue")
  • ThemeModelKey.Default is predefined for the default theme. If the current theme is not set, then ThemeModelKey.Default is used to retrieve the ThemeModel. Alternatively, you can also access the default ThemeModel using it.
DynamicThemeService.get().getThemeModel(ThemeModelKey.Default)



ThemeModel

The ThemeModel is a data class for saving themes. It contains three parameters:

  • ColorPalette: Light mode and dark mode color combinations for the theme.
  • Typography: Configurations of typography for the theme.
  • Shapes: Shape configurations for the theme."
data class ThemeModel(
    val colorPalette: ColorPalette = ColorPalette(),
    val typography: Typography = Typography(),
    val shapes: Shapes = Shapes()
)

ThemeModel looks same on Material2 and Material3, but inside ThemeModel is little bit different. It's caused by the classes used in Material2 and Material3 are different.

Check what your project is using and make ThemeModel


Material2 ThemeModel

Material2 ThemeModel consists of three parameters: ColorPalette, Typography, Shapes.

ColorPalette

The ColorPalette contains two parameters: 'lightModeColors' for the Light Theme and 'darkModeColors' for the Dark Theme. When the device is set to dark mode on system, the darkModeColors are automatically used.

data class ColorPalette(
    val lightModeColors: Colors = LightModeColorPalette,
    val darkModeColors: Colors = DarkModeColorPalette
)

Colors class is Material2 class which consist of primary, primaryVariant, secondary, secondaryVariant, background, surface, error, onPrimary, onSecondary, onBackground, onSurface, onError, isLight variables. You can find more about color system on Material2 Color System, Material2 Color Document

Typography

Typography contains design data for characters. This is also part of Material2 Typography System. You will probably see familiar things like h1, h2, h3, subtitle1, subtitle2, body1, body2. Which are used to build heirarchy of characters. See Material2 Typography

Shapes

See Material2 Shapes document



Material3 ThemeModel

Material3 ThemeModel consists of three parameters: ColorPalette, Typography, Shapes.

ColorPalette

The ColorPalette contains two parameters: 'lightModeColors' for the Light Theme and 'darkModeColors' for the Dark Theme. When the device is set to dark mode on system, the darkModeColors are automatically used.

data class ColorPalette(
    val lightModeColors: ColorScheme = DefaultLightColorPalette,
    val darkModeColors: ColorScheme = DefaultDarkColorPalette
)

ColorScheme class is Material3 class which consist of 29 colors which are used for coloring the application. You can find more about color system on Material3 Color System, Material3 Color Document

Typography

Typography contains design data for characters. This is also part of Material3 Typography System. Material3 typography enables us to fine-tune control for designing texts. See Material3 Typography

Shapes

See Material3 Shapes document



How to use DynamicThemeService

DynamicThemeService is initialized as a singleton object by using DynamicThemeService.init(applicationContext). You must initalize DynamicThemeService to use it.

class DynamicThemeApp : Application() {
    override fun onCreate() {
        super.onCreate()
        DynamicThemeService.init(applicationContext)
    }
}

After initialized DynamicThemeService can be accessed by using DynamicThemeService.get().

val dynamicThemeService: DynamicThemeService = DynamicThemeService.get()

DynamicThemeService features

Dynamic has following features:

  • Register Themes
  • Get Registered Themes
  • Set Current Theme
  • Get Current Theme
  • Provide Theme Composable with Current Theme
  • Provide Theme Composable with ThemeModel
  • How access theme variables on Composable

Register Themes

Registering both single and multiple theme supported. Default theme can also be changed.

Single Theme Registration

DynamicThemeService.get().apply {
    registerThemeModel(
        ThemeModelKey.of("Pink"), ThemeModel(
            colorPalette = ColorPalette(
                lightModeColors = ColorPalettes.PinkColorPalette,
                darkModeColors = ColorPalettes.PinkColorPalette,
            ),
            shapes = Shapes.DEFAULT_ROUNDED_SHAPE
        )
    )

    registerThemeModel(
        ThemeModelKey.of("Blue"), ThemeModel(
            colorPalette = ColorPalette(
                lightModeColors = ColorPalettes.BlueColorPalette,
                darkModeColors = ColorPalettes.BlueColorPalette,
            )
        )
    )
}

Multiple Theme Registration

DynamicThemeService.get().apply {
    registerThemeModels(ThemeModels.getSupportedThemeModels())
}

fun getSupportedThemeModels(): Map<ThemeModelKey, ThemeModel> = mapOf(
    THEME_MODEL_KEY_DEFAULT to THEME_MODEL_DEFAULT,
    THEME_MODEL_KEY_BLACK to THEME_MODEL_BLACK,
    THEME_MODEL_KEY_PINK to THEME_MODEL_PINK,
    THEME_MODEL_KEY_BLUE to THEME_MODEL_BLUE,
    THEME_MODEL_KEY_WHITE to THEME_MODEL_WHITE
)

Register Default Theme

DynamicThemeService.get().apply {
    registerThemeModel(
        ThemeModelKey.Default, ThemeModel(
            colorPalette = ColorPalette(
                lightModeColors = ColorPalettes.BlueColorPalette,
                darkModeColors = ColorPalettes.BlueColorPalette,
            )
        )
    )
}



Get Registered Themes

Get Single Registered Theme

DynamicThemeService.get().getThemeModel(themeModelKey)

Get All Registered Themes

val registeredThemes: Map<ThemeModelKey, ThemeModel> = DynamicThemeService.get().getRegisteredThemes()



Set Current Theme

suspend fun setCurrentTheme(themeModelKey: ThemeModelKey){
    DynamicThemeService.get().setCurrentTheme(themeModelKey)
}



Get Current Theme

Get Current Theme

val currentThemeModel: ThemeModel = async { DynamicThemeService.get().getCurrentThemeModel() }.await()

Get Current Theme with Reactively with Flow

val currentThemeModelFlow: Flow<ThemeModel> = DynamicThemeService.get().currentThemeModel



Provide Current Theme Composable

DynamicThemeService.get().ProvidesTheme {
    // Write Composable Functions
}



Provide Theme Composable with ThemeModel

DynamicThemeService.get().ProvidesTheme(themeModel) {
    // Write Composable Functions
}



How to access theme variables

Accessing theme variables in Material2

  • Accessing color can be done by using 'MaterialTheme.colors.[Color Name]'
Box(
    modifier = Modifier
        .fillMaxSize()
        .background(MaterialTheme.colors.background)
)
  • Accessing typography can be done by using 'MaterialTheme.typography.[Typography Name]'
Text(
    text = "Themes",
    style = MaterialTheme.typography.h5
)
  • Accessing shapes can be done by using 'MaterialTheme.shapes.[Shape Name]'
Box(
    modifier = Modifier
        .fillMaxSize()
        .clip(MaterialTheme.shapes.medium)
)

Accessing theme variables in Material3

  • Accessing color can be done by using 'MaterialTheme.colorScheme.[Color Name]'
Box(
    modifier = Modifier
        .fillMaxSize()
        .background(MaterialTheme.colorScheme.background)
)
  • Accessing typography can be done by using 'MaterialTheme.typography.[Typography Name]'
Text(
    text = "Themes",
    style = MaterialTheme.typography.bodyLarge
)
  • Accessing shapes can be done by using 'MaterialTheme.shapes.[Shape Name]'
Card(
    modifier = modifier,
    shape = MaterialTheme.shapes.extraLarge
)



Find this repository useful?👍

  • Support it by making star⭐!
    You can see stargazers here.
  • Also, follow me on GitHub for further updates

License

Designed and developed by 2023 seyoungcho2 (Seyoung Cho)

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.