9

How would you dynamically switch between theme's color palette with a press of a button inside the app

This is what I am doing so far, but only works when I switch the Android Theme to dark or light mode

AppTheme.Kt

@Model
object ThemeState {
    var isLight: Boolean = true
}


@Composable
fun MyAppTheme(
    children: @Composable() () -> Unit
) {
    MaterialTheme(colors = if (ThemeState.isLight) themeColorsLight else themColorDark) {
            children()
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme(children = {
                Surface {
                    Greetings(name = "Android")
                }

            })

        }
    }
}

@Composable
fun Greetings(name: String) {

    Column(modifier = Modifier.fillMaxHeight()) {
        Column(modifier = Modifier.weight(1f)) {
            Text(
                text = "Hello $name", modifier = Modifier.padding(24.dp),
                style = MaterialTheme.typography.h1
            )
        }

        Button(onClick = { ThemeState.isLight = !ThemeState.isLight }) {
            Text(text = "Change Theme IsLight:${ThemeState.isLight}")
        }
    }
}

Brown
  • 121
  • 3
  • 10
  • is that answer to your question: https://stackoverflow.com/questions/11562051/change-activitys-theme-programmatically – tohid noori Jun 01 '20 at 20:00
  • Not really because that is just to change the theme which I understand how to do. I am having trouble using jetpack compose. Specifically, I do not understand why my MaertailThemes comparable is not recomposed when the data it is subscribed to changes – Brown Jun 01 '20 at 20:16

2 Answers2

14

At the moment I don't have any Idea why your code not works, I'll update this answer when I find out.

but instead of using if else for colors parameter use it for the whole MaterialTheme like this and it will work:

@Composable
fun MyAppTheme(
    children: @Composable() () -> Unit
) {

    if (ThemeState.isLight) {
        MaterialTheme(colors = themeColorsLight) {
            children()
        }
    } else {
        MaterialTheme(colors = themColorDark) {
            children()
        }

    }
}

Update: seems that it's bug in Jetpack Compose dev11, I tried in dev12 and it works there.

NOTE 1: @Model has been deprecated in dev 12 change your ThemeState to

object ThemeState {
    var isLight by mutableStateOf(true)
}

more information: https://android-review.googlesource.com/c/platform/frameworks/support/+/1311293

NOTE 2 There are some problems with auto Import in recent versions of AndroidStudio If the Idea throws error: Type 'MutableState<TypeVariable(T)>' has no method 'getValue(ThemeState, KProperty<*>)' and thus it cannot serve as a delegate

Import getValue and SetValue manually.

import androidx.compose.getValue
import androidx.compose.setValue

Since 0.1.0-dev16 use these imports:

import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue

WernerD
  • 108
  • 1
  • 7
Habib Kazemi
  • 2,172
  • 1
  • 24
  • 30
4

Use AppCompatDelegate class

Step 1: Define a state that will point to Light mode initially.

object ThemeState {
 var darkModeState : MutableState<Boolean> = mutableStateOf(false)
}

Note : Whenever this state will be changed, all the methods reading this state value will also be called.

Step 2 : Define a variable for reading state

val isDark = ThemeState.darkModeState.value

Step 3 : Now change Theme mode from Dark to Light and vice versa as follows

Button(onClick = {

            val theme = when(isDark){
                true -> AppCompatDelegate.MODE_NIGHT_NO
                false -> AppCompatDelegate.MODE_NIGHT_YES
            }
            AppCompatDelegate.setDefaultNightMode(theme)
            ThemeState.darkModeState.value = !isDark

        }) {
            Text(text = "Theme Toggle Button")
        }

As you can see here, I'm changing app theme every time Theme Toggle Button is clicked.

Ankit Dubey
  • 1,000
  • 1
  • 8
  • 12