Підтримка динамічних тем оформлення мобільного застосунку
Динамічні теми — це коли користувач може сам вибирати тему оформлення: вибрати з кількох палітр, задати акцентний колір, переключитися між світлою та темною без перезапуску. Це складніше, ніж просто 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 днів |
Вартість розраховується індивідуально після аналізу архітектури проекту.







