Tran Hoai Nam
Tran Hoai Nam

Reputation: 1363

Dynamic theme color at run time jetpack compose

I'm new to Jetpack Compose, so I'm struggling to implement a feature which is dynamic colors (and font, size,... but I think they are the same so I'll just focus on color) at run time from backend. I'll let the app the some default colors, and a whole default splash screen just to load the colors setting from the backend. In case the API request failed, it would use the last succeeded requested colors or just the default color.

Tutorials I found round the internet was just about changing the dark/light theme at run time, not changing a specific color in the color pack. In those tutorials, the color is defined in Colors.kt file which is not a composable or class or object, ...

I imagine the color within lightColors or darkColors would be something like this.

return lightColors(
        primary = Color(android.graphics.Color.parseColor("#" + dynamicColorMap["One"])),
...
}

And when dynamicColorMap changes in the splashscreen, all screen later will have reference to the new value, but I don't know how to update its variable outside of a composable.

I thought of using DB to store the colors, but getting the data from DB is async, so it cannot be query in the default Colors.kt like var colorOne = DBManager.getColor("One"), I can run the async task in my splash screen before changing to the next screen but then the problem again is how to have a global state that my theme composable wrapper can have access to on every screen?

I just don't know where to start for these case.

Thank you for your time

EDIT: I currently having the project structured in MVVM. For now, only one activity (MainActivity) is present, and inside that activity, the splash screen component or home screen or login screen,... are being navigated. So is it a good practice to create a viewmodel for the mainactivity screen, that can holds the color state for the theme?

Upvotes: 1

Views: 2287

Answers (1)

Tran Hoai Nam
Tran Hoai Nam

Reputation: 1363

Thanks @Maciej Ciemiega for the suggestion. I ended up structure my code like that.

In my MainActivity.kt I create a viewmodel for it.

val mainActivityViewModel by viewModels<MainActivityViewModel>()
MyTheme(mainActivityViewModel = mainActivityViewModel) {
                initNavigationController(navController)
                Surface(color = MaterialTheme.colors.background) {
                    if (mainActivityViewModel.appSettingsState.value.appSettings.colorsMapLight.size != 0
                        && mainActivityViewModel.appSettingsState.value.appSettings.colorsMapDark.size != 0) {
                        navController.navigate(NavigationDestinations.homeScreen)
                    }
                }
            }

my initNavigationController function shows the splashscreen first. But it doesn't do anything. The getting app settings configuration is called in MyTheme composable via the mainActivityViewModel, and MyTheme will use the state from the viewmodel to define the theme, and the navController.navigate is based on the state as you guys can see in the if above.

I don't know if this is a good practice or not, or when my app grows it would be a mess or not, but at least it works for me. I tried with font styles too and it works like a charm.

Upvotes: 1

Related Questions