Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to switch between light and dark theme dynamically in app using composables

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}")
        }
    }
}

like image 544
Brown Avatar asked Jun 01 '20 17:06

Brown


2 Answers

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

like image 91
Habib Kazemi Avatar answered Oct 06 '22 23:10

Habib Kazemi


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.

like image 3
Ankit Dubey Avatar answered Oct 07 '22 00:10

Ankit Dubey