3

I am trying to implement dark theme in my app. I am using Android Preferences DataStore to save user preference for theme. The datastore class exposes a flow for preferred theme themeFLow and a suspend function updateTheme() to update the preferred theme.

I have a ThemeManager class to manage theme changes. It looks like this.

class ThemeManager {
    
    private val currentTheme = themeDataStore.themeFlow
    
    init {
        scope.launch(Dispatchers.Main) {
            currentTheme.collect {
                AppCompatDelegate.setDefaultNightMode(it)
            }
        }
    }

    fun setCurrentTheme(newTheme: Int) {
        scope.launch {
            themeDataStore.updateTheme(newTheme)
        }
    }
}

In UI, I display a dialog box to choose current theme and call themeManager.setCurrentTheme(newTheme) The problem is that theme changes with a flicker. See video (slowed down)

Other SO answers on this question (like this and this) provide a solution which involves finishing and restarting the activity. In my app, I am following a single activity + fragments architecture (using Jetpack Navigation Library), so when I restart activity like this, activity starts from home fragment and the current destination is lost.

Is there some way to get rid of this flickering?

Arpit Shukla
  • 9,612
  • 1
  • 14
  • 40

2 Answers2

0

On click of some button / trigger, you can use below code in the fragment to recreate current activity:

val intent = activity?.intent
intent?.putExtra(EXTRA_KEY_IS_THEME_CHANGED, true)
activity?.finish()
startActivity(intent)
activity?.overridePendingTransition(0, 0)

I have tried this in my project and it works fine without flickering. You can even check for the boolean EXTRA_KEY_IS_THEME_CHANGED to navigate to the specific fragment with the help of navigation.

navController.navigate(R.id.fragment_you_need_to_show)
0

I have faced same issue when i changed the whole app language, in my application i have one navigation host activity and rest are fragment like you.

override fun recreate() {
    finish()
    overridePendingTransition(
        R.anim.empty_animation,
        R.anim.empty_animation
    )
    startActivity(intent)
    overridePendingTransition(
        R.anim.empty_animation,
        R.anim.empty_animation
    )
}

Paste it in your base or navigation host activity, it works for me. I have given empty animation according to my requirements depend on you, you can give fade in or fade out animations for beauty.