Dynamic Theme Support for Mobile Applications
Dynamic themes are when users can choose their own theme: select from several palettes, set an accent color, switch between light and dark without restart. This is more complex than just dark/light mode: runtime theme switching must apply instantly to all UI without screen flicker.
Theme System Architecture
Key decision: how to store the current theme and pass it to components.
iOS (SwiftUI): @Environment plus custom EnvironmentKey. Create AppTheme as ObservableObject, publish via environmentObject, all components read via @EnvironmentObject var theme: AppTheme. When theme.colorScheme changes, SwiftUI automatically redraws the entire tree. Switching is instant, without UIApplication.shared.windows hacks.
iOS (UIKit): More complex. UIAppearance proxy for global settings plus traitCollection override. Or custom ThemeManager via Notification Center: when theme changes, all subscribed components call applyTheme(). Minus: need explicit unsubscribe, easy to catch retain cycle.
Android (Compose): MaterialTheme(colorScheme = currentColorScheme) at composition root. CompositionLocalProvider(LocalAppTheme provides theme). When remember { mutableStateOf(lightColorScheme) } changes, Compose recomposes only subtrees that read the theme. Very efficient.
React Native: ThemeContext via React Context API plus useContext. Or ready solution via styled-components/native with ThemeProvider. When theme changes, all subscribed components rerender. To prevent excess renders — React.memo plus useMemo for theme object.
Flutter: MaterialApp(theme: lightTheme, darkTheme: darkTheme, themeMode: themeMode). Custom themes — ThemeExtension<T>. ThemeMode.system / .light / .dark managed via setState or Provider/Riverpod.
User Accent Color
Android 12+ supports Material You — dynamic palette generated from user wallpaper via DynamicColors.applyToActivitiesIfAvailable(this). Result: app automatically adapts colors to phone personalization.
For custom color picker inside the app: need to generate complete ColorScheme from selected seed color. On Android this is dynamicDarkColorScheme / dynamicLightColorScheme (API 31+) or material-color-utilities library for API <31. On iOS — manual calculation of derivative colors via HSL.
Persistence and Switching Without Restart
Theme choice must be saved. iOS: UserDefaults plus @AppStorage in SwiftUI. Android: DataStore<Preferences> (recommended over SharedPreferences). React Native: AsyncStorage or MMKV for synchronous access. Flutter: SharedPreferences or Hive.
Critical moment: on first launch, theme must be applied before user sees the first frame. Otherwise there's flash of wrong theme — screen flickers from default theme to saved. On iOS solved by synchronous read from UserDefaults in AppDelegate / @main before window rendering. On Android — via SplashScreen API with correct background color.
Switching Test
Test that reveals most problems: open complex UI screen → switch theme 5–10 times quickly → verify no flicker, memory leak (Instruments / Android Profiler), all colors applied correctly.
Special attention: UIAlertController, UIActivityViewController, system components on iOS — they don't always respond to custom themes and require separate handling.
| Support Type | Timeline |
|---|---|
| Just dark/light switching | 1–2 days |
| Several ready themes to choose from | 2–3 days |
| Dynamic accent color | 3–5 days |
| Material You (Android) + full system | 4–6 days |
Cost is calculated individually after project architecture analysis.







