Sobes.tech
Junior

How to save and use theme settings in an Android application?

sobes.tech AI

Answer from AI

An effective approach involves using SharedPreferences to save the selected theme and AppCompatDelegate.setDefaultNightMode() to apply it.

  1. Saving the theme: Use SharedPreferences to store the identifier of the chosen theme (e.g., "light", "dark", "system").

    // Get SharedPreferences
    val sharedPrefs = getSharedPreferences("AppTheme", Context.MODE_PRIVATE)
    // Get editor
    val editor = sharedPrefs.edit()
    // Apply theme with ID themeId
    editor.putString("current_theme", themeId)
    // Save changes
    editor.apply()
    
  2. Applying the theme at startup: In the main Activity or Application class, read the saved value from SharedPreferences and apply the theme.

    // Read the saved theme value
    val themeId = getSharedPreferences("AppTheme", Context.MODE_PRIVATE)
        .getString("current_theme", "system") // default to 'system'
    
    // Apply the theme
    when (themeId) {
        "light" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
        "dark" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
        "system" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
        else -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
    }
    
  3. Changing the theme by the user: Provide a user interface (e.g., RadioButton in settings) where the user can select a theme. When selected, save the new identifier in SharedPreferences and immediately apply the theme by calling AppCompatDelegate.setDefaultNightMode() and, if necessary, restart the Activity for proper theme application.

    // Suppose the user selected a dark theme
    val sharedPrefs = getSharedPreferences("AppTheme", Context.MODE_PRIVATE)
    val editor = sharedPrefs.edit()
    editor.putString("current_theme", "dark")
    editor.apply()
    
    // Apply the theme immediately
    AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
    
    // Or recreate() for full theme application
    // recreate()
    

    Restarting the Activity with recreate() may be necessary to ensure all Views in the current Activity are correctly redrawn with the new theme.

  4. Defining themes in styles.xml: Define styles for light and dark themes in res/values/styles.xml and res/values-night/styles.xml, inheriting from Theme.AppCompat.

    <!-- res/values/styles.xml -->
    <style name="AppTheme.Light" parent="Theme.AppCompat.Light">
        <!-- Style definitions for light theme -->
    </style>
    
    <!-- res/values-night/styles.xml -->
    <style name="AppTheme.Dark" parent="Theme.AppCompat">
        <!-- Style definitions for dark theme -->
    </style>
    

    In AndroidManifest.xml, specify the base theme of the application:

    <!-- AndroidManifest.xml -->
    <application
        android:theme="@style/AppTheme.Light"
        ...>
        <!-- ... Activities -->
    </application>
    
  5. Listening for theme changes: In case the system theme changes (e.g., on schedule or manually via phone settings), you can use Configuration.uiMode in onConfigurationChanged() to respond, but AppCompatDelegate usually handles this automatically when using MODE_NIGHT_FOLLOW_SYSTEM.

    @Override
    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        // You can check the new theme if needed
        // val currentNightMode = newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK
        // ...
    }
    

Using AppCompatDelegate is the recommended approach for theme support on Android, as it provides backward compatibility and proper working with various support library components.