Поддержка динамических тем оформления мобильного приложения
Динамические темы — это когда пользователь может сам выбирать тему оформления: выбрать из нескольких палитр, задать акцентный цвет, переключиться между светлой и тёмной без перезапуска приложения. Это сложнее, чем просто dark/light mode: нужна runtime-смена темы с мгновенным применением ко всему UI без мигания экрана.
Архитектура системы тем
Ключевое решение — как хранить текущую тему и как передавать её в компоненты.
iOS (SwiftUI): @Environment + кастомный EnvironmentKey. Создаём AppTheme как ObservableObject, публикуем через environmentObject, все компоненты читают через @EnvironmentObject var theme: AppTheme. При изменении theme.colorScheme SwiftUI автоматически перерисовывает всё дерево. Переключение мгновенное, без UIApplication.shared.windows хаков.
iOS (UIKit): Сложнее. UIAppearance proxy для глобальных настроек + traitCollection override. Или кастомный ThemeManager через Notification Center: при смене темы все подписанные компоненты вызывают applyTheme(). Минус: нужно явно отписываться, легко словить retain cycle.
Android (Compose): MaterialTheme(colorScheme = currentColorScheme) в корне composition. CompositionLocalProvider(LocalAppTheme provides theme). При изменении remember { mutableStateOf(lightColorScheme) } Compose перекомпозирует только те поддеревья, которые читают тему. Очень эффективно.
React Native: ThemeContext через React Context API + useContext. Или готовое решение через styled-components/native с ThemeProvider. При смене темы все компоненты, подписанные на контекст, перерендериваются. Для предотвращения лишних рендеров — React.memo + useMemo для объекта темы.
Flutter: MaterialApp(theme: lightTheme, darkTheme: darkTheme, themeMode: themeMode). Кастомные темы — ThemeExtension<T>. ThemeMode.system / .light / .dark управляется через setState или Provider/Riverpod.
Пользовательский акцентный цвет
Android 12+ поддерживает Material You — динамическая палитра генерируется из обоев пользователя через DynamicColors.applyToActivitiesIfAvailable(this). Результат: приложение автоматически адаптирует цвета под персонализацию телефона.
Для кастомного colour picker внутри приложения: нужно генерировать полную ColorScheme из выбранного seed-цвета. На Android это dynamicDarkColorScheme / dynamicLightColorScheme (API 31+) или библиотека material-color-utilities для API <31. На iOS — ручной расчёт производных цветов через HSL.
Персистентность и смена без перезапуска
Выбор темы нужно сохранять. iOS: UserDefaults + @AppStorage в SwiftUI. Android: DataStore<Preferences> (рекомендуется над SharedPreferences). React Native: AsyncStorage или MMKV для синхронного доступа. Flutter: SharedPreferences или Hive.
Критичный момент: при первом запуске тема должна применяться до того, как пользователь увидит первый кадр. Иначе будет flash of wrong theme — экран мигает из дефолтной темы в сохранённую. На iOS решается синхронным чтением из UserDefaults в AppDelegate / @main до отрисовки окна. На Android — через SplashScreen API с правильным цветом фона.
Тестирование переключения
Тест, который выявляет большинство проблем: открыть экран со сложным UI → переключить тему 5–10 раз быстро → убедиться, что нет мигания, утечки памяти (Instruments / Android Profiler), и все цвета применились корректно.
Особое внимание: UIAlertController, UIActivityViewController, системные компоненты на iOS — они не всегда реагируют на кастомные темы и требуют отдельной обработки.
| Вид поддержки | Срок |
|---|---|
| Только dark/light переключение | 1–2 дня |
| Несколько готовых тем на выбор | 2–3 дня |
| Динамический акцентный цвет | 3–5 дней |
| Material You (Android) + полная система | 4–6 дней |
Стоимость рассчитывается индивидуально после анализа архитектуры проекта.







