Home > Blockchain >  Jetpack Compose. Force switch night/notnight resources
Jetpack Compose. Force switch night/notnight resources

Time:10-09

Initially, the theme of my application followed the theme of the system, for convenience I created drawable resources for light and dark themes. Then I implemented the ability to change the theme manually regardless of the system theme, but apparently the drawable resources are tied to the system theme or something else. Is there a way to force-switch between night and notnight drawable resources somehow?

CodePudding user response:

What you can do is use Data Store and assign the value of that toggle to it. Then in your setContent {} you can check it as :

val theme = userSettings.themeStream.collectAsState()
            val useDarkColors = when (theme.value) {
                AppTheme.MODE_AUTO -> isSystemInDarkTheme()
                AppTheme.MODE_DAY -> false
                AppTheme.MODE_NIGHT -> true
            }

Where userSettings is :

enum class AppTheme {
    MODE_DAY,
    MODE_NIGHT,
    MODE_AUTO;

    companion object {
        fun fromOrdinal(ordinal: Int) = values()[ordinal]
    }
}

interface UserSettings {
    val themeStream: StateFlow<AppTheme>
    var theme: AppTheme
}

class UserSettingsImpl @Inject constructor(
    @ApplicationContext context: Context
) : UserSettings {

    override val themeStream: MutableStateFlow<AppTheme>
    override var theme: AppTheme by AppThemePreferenceDelegate("app_theme", AppTheme.MODE_AUTO)

    private val preferences: SharedPreferences =
        context.getSharedPreferences("sample_theme", Context.MODE_PRIVATE)

    init {
        themeStream = MutableStateFlow(theme)
    }

    inner class AppThemePreferenceDelegate(
        private val name: String,
        private val default: AppTheme,
    ) : ReadWriteProperty<Any?, AppTheme> {

        override fun getValue(thisRef: Any?, property: KProperty<*>): AppTheme =
            AppTheme.fromOrdinal(preferences.getInt(name, default.ordinal))

        override fun setValue(thisRef: Any?, property: KProperty<*>, value: AppTheme) {
            themeStream.value = value
            preferences.edit {
                putInt(name, value.ordinal)
            }
        }
    }
}

Then it should take the one stored in Data Store instead of using the system one.

Here's a full example Compose theme switcher from Stefano Sansone

Also you could take a read on Jetpack Compose Theming

CodePudding user response:

Found a solution, correct me if something is wrong.

  1. Inherit our Activity from AppCompatActivity instead of ComponentActivity

  2. Use AppDelegateCompat.setDefaultNightMode() tell the application which theme we are going to use

  3. Call delegate.applyDayNight()

     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         // Getting user's saved selection
         appTheme.value = runBlocking(Dispatchers.IO) {
             getSavedAppTheme(context)
         }
         // Telling the application which mode to use
         when (appTheme.value) {
             AppTheme.SYSTEM_THEME -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
             AppTheme.NIGHT_THEME  -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
             AppTheme.LIGHT_THEME  -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
         }
         delegate.applyDayNight()
         setContent {
             MyAppTheme {
                 MyAppComposable()
             }
         }
     }
    

Handling user selection:

    suspend fun setAppTheme(context: Context, theme: AppTheme) {
        // Saving selection to cache 
        saveAppTheme(context, theme)
        // Applying selection
        appTheme.value = theme
        // Telling the application which mode to use
        when (appTheme.value) {
            AppTheme.SYSTEM_THEME -> setDefaultNightMode(MODE_NIGHT_FOLLOW_SYSTEM)
            AppTheme.NIGHT_THEME  -> setDefaultNightMode(MODE_NIGHT_YES)
            AppTheme.LIGHT_THEME  -> setDefaultNightMode(MODE_NIGHT_NO)
        }
    }

UPD:

Point 2 is better to call in Application.onCreate() so as not to reload Activity inside onCreate()

  • Related