theme_manager.dart 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. import 'package:flutter/foundation.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:shared_preferences/shared_preferences.dart';
  4. import 'theme_notifier.dart';
  5. import '../l10n/app_localizations.dart';
  6. /// Seasonal theme system for ZenTap
  7. enum SeasonalTheme {
  8. automatic('automatic'),
  9. default_('default'),
  10. spring('spring'),
  11. summer('summer'),
  12. autumn('autumn'),
  13. winter('winter');
  14. const SeasonalTheme(this.id);
  15. final String id;
  16. static SeasonalTheme fromId(String id) {
  17. return SeasonalTheme.values.firstWhere(
  18. (theme) => theme.id == id,
  19. orElse: () => SeasonalTheme.automatic,
  20. );
  21. }
  22. }
  23. /// Theme color scheme for each season
  24. class SeasonalColors {
  25. final Color appBackground;
  26. final Color secondaryBackground;
  27. final Color primaryText;
  28. final Color secondaryText;
  29. final Color mutedText;
  30. final Color buttonBackground;
  31. final Color buttonText;
  32. final Color uiElements;
  33. final Color gameBoardBorder;
  34. final Color bubbleDefault;
  35. final Color bubblePopEffect;
  36. final Color scoreText;
  37. final Color timerText;
  38. final Color links;
  39. final Color linkHover;
  40. final Color selectedMenuItem;
  41. // Animation colors
  42. final List<Color> animationLayer1;
  43. final List<Color> animationLayer2;
  44. final List<Color> animationLayer3;
  45. final List<Color> zenModeColors;
  46. const SeasonalColors({
  47. required this.appBackground,
  48. required this.secondaryBackground,
  49. required this.primaryText,
  50. required this.secondaryText,
  51. required this.mutedText,
  52. required this.buttonBackground,
  53. required this.buttonText,
  54. required this.uiElements,
  55. required this.gameBoardBorder,
  56. required this.bubbleDefault,
  57. required this.bubblePopEffect,
  58. required this.scoreText,
  59. required this.timerText,
  60. required this.links,
  61. required this.linkHover,
  62. required this.selectedMenuItem,
  63. required this.animationLayer1,
  64. required this.animationLayer2,
  65. required this.animationLayer3,
  66. required this.zenModeColors,
  67. });
  68. }
  69. class ThemeManager {
  70. static const String _keySelectedTheme = 'selected_theme';
  71. static SharedPreferences? _prefs;
  72. static SeasonalTheme _currentTheme = SeasonalTheme.automatic;
  73. static Future<void> init() async {
  74. _prefs = await SharedPreferences.getInstance();
  75. final themeId = _prefs?.getString(_keySelectedTheme) ?? 'automatic';
  76. _currentTheme = SeasonalTheme.fromId(themeId);
  77. ThemeNotifier().initFromManager();
  78. }
  79. static SeasonalTheme get currentTheme => _currentTheme;
  80. static Future<void> setTheme(SeasonalTheme theme) async {
  81. _currentTheme = theme;
  82. await _prefs?.setString(_keySelectedTheme, theme.id);
  83. ThemeNotifier().setTheme(theme);
  84. }
  85. /// Detect current season based on date
  86. static SeasonalTheme _detectCurrentSeason() {
  87. final now = DateTime.now();
  88. final month = now.month;
  89. final day = now.day;
  90. // Northern hemisphere seasons
  91. if ((month == 3 && day >= 20) ||
  92. month == 4 ||
  93. month == 5 ||
  94. (month == 6 && day < 21)) {
  95. return SeasonalTheme.spring; // March 20 - June 20
  96. } else if ((month == 6 && day >= 21) ||
  97. month == 7 ||
  98. month == 8 ||
  99. (month == 9 && day < 22)) {
  100. return SeasonalTheme.summer; // June 21 - September 21
  101. } else if ((month == 9 && day >= 22) ||
  102. month == 10 ||
  103. month == 11 ||
  104. (month == 12 && day < 21)) {
  105. return SeasonalTheme.autumn; // September 22 - December 20
  106. } else {
  107. return SeasonalTheme.winter; // December 21 - March 19
  108. }
  109. }
  110. /// Get the effective theme (resolves automatic to actual season)
  111. static SeasonalTheme get effectiveTheme {
  112. if (_currentTheme == SeasonalTheme.automatic) {
  113. return _detectCurrentSeason();
  114. }
  115. return _currentTheme;
  116. }
  117. /// Get the auto-detected season (regardless of current theme setting)
  118. static SeasonalTheme get autoDetectedSeason {
  119. return _detectCurrentSeason();
  120. }
  121. /// Get the current theme's color scheme
  122. static SeasonalColors get colors {
  123. final theme = effectiveTheme;
  124. switch (theme) {
  125. case SeasonalTheme.spring:
  126. return _springColors;
  127. case SeasonalTheme.summer:
  128. return _summerColors;
  129. case SeasonalTheme.autumn:
  130. return _autumnColors;
  131. case SeasonalTheme.winter:
  132. return _winterColors;
  133. case SeasonalTheme.default_:
  134. return _defaultColors;
  135. case SeasonalTheme.automatic:
  136. // This should never happen since effectiveTheme resolves it
  137. return _detectCurrentSeason() == SeasonalTheme.spring
  138. ? _springColors
  139. : _detectCurrentSeason() == SeasonalTheme.summer
  140. ? _summerColors
  141. : _detectCurrentSeason() == SeasonalTheme.autumn
  142. ? _autumnColors
  143. : _winterColors;
  144. }
  145. }
  146. /// Get theme display name
  147. static String getThemeDisplayName(SeasonalTheme theme, BuildContext context) {
  148. final l10n = AppLocalizations.of(context)!;
  149. switch (theme) {
  150. case SeasonalTheme.automatic:
  151. return l10n.automaticTheme;
  152. case SeasonalTheme.default_:
  153. return l10n.defaultTheme;
  154. case SeasonalTheme.spring:
  155. return l10n.springTheme;
  156. case SeasonalTheme.summer:
  157. return l10n.summerTheme;
  158. case SeasonalTheme.autumn:
  159. return l10n.autumnTheme;
  160. case SeasonalTheme.winter:
  161. return l10n.winterTheme;
  162. }
  163. }
  164. /// Get theme icon
  165. static IconData getThemeIcon(SeasonalTheme theme) {
  166. switch (theme) {
  167. case SeasonalTheme.automatic:
  168. return Icons.auto_mode;
  169. case SeasonalTheme.default_:
  170. return Icons.contrast;
  171. case SeasonalTheme.spring:
  172. return Icons.local_florist;
  173. case SeasonalTheme.summer:
  174. return Icons.wb_sunny;
  175. case SeasonalTheme.autumn:
  176. return Icons.park;
  177. case SeasonalTheme.winter:
  178. return Icons.ac_unit;
  179. }
  180. }
  181. /// Get available themes for selection (excluding default in release mode)
  182. static List<SeasonalTheme> get availableThemes {
  183. return SeasonalTheme.values.where((theme) {
  184. // Hide default theme in release builds
  185. if (!kDebugMode && theme == SeasonalTheme.default_) {
  186. return false;
  187. }
  188. return true;
  189. }).toList();
  190. }
  191. /// Default fSociety theme (current colors)
  192. static const SeasonalColors _defaultColors = SeasonalColors(
  193. appBackground: Color(0xFF000000),
  194. secondaryBackground: Color(0xFF0A0A0A),
  195. primaryText: Color(0xFFFFFFFF),
  196. secondaryText: Color(0xDEFFFFFF),
  197. mutedText: Color(0xFF808080),
  198. buttonBackground: Color(0xFF0BC2F9),
  199. buttonText: Color(0xFFFFFFFF),
  200. uiElements: Color(0xFF808080),
  201. gameBoardBorder: Color(0xFF808080),
  202. bubbleDefault: Color(0xFF646CFF),
  203. bubblePopEffect: Color(0xFFFF0000),
  204. scoreText: Color(0xFFFFFFFF),
  205. timerText: Color(0xDEFFFFFF),
  206. links: Color(0xFF000080),
  207. linkHover: Color(0xFFFF0000),
  208. selectedMenuItem: Color(0xFFFFFFFF),
  209. animationLayer1: [Color(0x33808080), Colors.transparent],
  210. animationLayer2: [Color(0x1AFF0000), Colors.transparent],
  211. animationLayer3: [Color(0x0D0BC2F9), Colors.transparent],
  212. zenModeColors: [Color(0x4D000080), Colors.transparent],
  213. );
  214. /// Spring theme - Fresh greens and soft pastels
  215. static const SeasonalColors _springColors = SeasonalColors(
  216. appBackground: Color(0xFF0A1A0A), // Dark forest green
  217. secondaryBackground: Color(0xFF1A2A1A),
  218. primaryText: Color(0xFFE8F5E8), // Light mint
  219. secondaryText: Color(0xDEE8F5E8),
  220. mutedText: Color(0xFF7FB069), // Soft green
  221. buttonBackground: Color(0xFF8FBC8F), // Dark sea green
  222. buttonText: Color(0xFF0A1A0A),
  223. uiElements: Color(0xFF556B2F), // Dark olive green
  224. gameBoardBorder: Color(0xFF6B8E23),
  225. bubbleDefault: Color(0xFF98FB98), // Pale green
  226. bubblePopEffect: Color(0xFFFFB6C1), // Light pink
  227. scoreText: Color(0xFFE8F5E8),
  228. timerText: Color(0xDEE8F5E8),
  229. links: Color(0xFF228B22), // Forest green
  230. linkHover: Color(0xFFFF69B4), // Hot pink
  231. selectedMenuItem: Color(0xFFE8F5E8),
  232. animationLayer1: [Color(0x3398FB98), Colors.transparent], // Pale green
  233. animationLayer2: [Color(0x1AFFB6C1), Colors.transparent], // Light pink
  234. animationLayer3: [Color(0x0D7FB069), Colors.transparent], // Soft green
  235. zenModeColors: [Color(0x4D228B22), Colors.transparent], // Forest green
  236. );
  237. /// Summer theme - Bright blues and warm yellows
  238. static const SeasonalColors _summerColors = SeasonalColors(
  239. appBackground: Color(0xFF0A0A2A), // Deep navy
  240. secondaryBackground: Color(0xFF1A1A3A),
  241. primaryText: Color(0xFFF0F8FF), // Alice blue
  242. secondaryText: Color(0xDEF0F8FF),
  243. mutedText: Color(0xFF87CEEB), // Sky blue
  244. buttonBackground: Color(0xFFFFD700), // Gold
  245. buttonText: Color(0xFF0A0A2A),
  246. uiElements: Color(0xFF4682B4), // Steel blue
  247. gameBoardBorder: Color(0xFF5F9EA0),
  248. bubbleDefault: Color(0xFF00BFFF), // Deep sky blue
  249. bubblePopEffect: Color(0xFFFF6347), // Tomato
  250. scoreText: Color(0xFFF0F8FF),
  251. timerText: Color(0xDEF0F8FF),
  252. links: Color(0xFF1E90FF), // Dodger blue
  253. linkHover: Color(0xFFFF4500), // Orange red
  254. selectedMenuItem: Color(0xFFF0F8FF),
  255. animationLayer1: [Color(0x33FFD700), Colors.transparent], // Gold
  256. animationLayer2: [Color(0x1AFFFF00), Colors.transparent], // Yellow
  257. animationLayer3: [Color(0x0DFFA500), Colors.transparent], // Orange
  258. zenModeColors: [Color(0x4D1E90FF), Colors.transparent], // Dodger blue
  259. );
  260. /// Autumn theme - Warm oranges, reds, and browns
  261. static const SeasonalColors _autumnColors = SeasonalColors(
  262. appBackground: Color(0xFF2A1A0A), // Dark brown
  263. secondaryBackground: Color(0xFF3A2A1A),
  264. primaryText: Color(0xFFFFF8DC), // Cornsilk
  265. secondaryText: Color(0xDEFFF8DC),
  266. mutedText: Color(0xFFDEB887), // Burlywood
  267. buttonBackground: Color(0xFFD2691E), // Chocolate
  268. buttonText: Color(0xFFFFF8DC),
  269. uiElements: Color(0xFF8B4513), // Saddle brown
  270. gameBoardBorder: Color(0xFFA0522D),
  271. bubbleDefault: Color(0xFFFF7F50), // Coral
  272. bubblePopEffect: Color(0xFFDC143C), // Crimson
  273. scoreText: Color(0xFFFFF8DC),
  274. timerText: Color(0xDEFFF8DC),
  275. links: Color(0xFFB22222), // Fire brick
  276. linkHover: Color(0xFFFF4500), // Orange red
  277. selectedMenuItem: Color(0xFFFFF8DC),
  278. animationLayer1: [Color(0x33FF7F50), Colors.transparent], // Coral
  279. animationLayer2: [Color(0x1ADC143C), Colors.transparent], // Crimson
  280. animationLayer3: [Color(0x0DDEB887), Colors.transparent], // Burlywood
  281. zenModeColors: [Color(0x4DB22222), Colors.transparent], // Fire brick
  282. );
  283. /// Winter theme - Cool blues and icy whites
  284. static const SeasonalColors _winterColors = SeasonalColors(
  285. appBackground: Color(0xFF0A1A2A), // Dark slate blue
  286. secondaryBackground: Color(0xFF1A2A3A),
  287. primaryText: Color(0xFFF0F8FF), // Alice blue
  288. secondaryText: Color(0xDEF0F8FF),
  289. mutedText: Color(0xFFB0C4DE), // Light steel blue
  290. buttonBackground: Color(0xFF6495ED), // Cornflower blue
  291. buttonText: Color(0xFF0A1A2A),
  292. uiElements: Color(0xFF708090), // Slate gray
  293. gameBoardBorder: Color(0xFF778899),
  294. bubbleDefault: Color(0xFF87CEFA), // Light sky blue
  295. bubblePopEffect: Color(0xFFFFFFFF), // White
  296. scoreText: Color(0xFFF0F8FF),
  297. timerText: Color(0xDEF0F8FF),
  298. links: Color(0xFF4169E1), // Royal blue
  299. linkHover: Color(0xFF00CED1), // Dark turquoise
  300. selectedMenuItem: Color(0xFFF0F8FF),
  301. animationLayer1: [Color(0x3387CEFA), Colors.transparent], // Light sky blue
  302. animationLayer2: [Color(0x1AFFFFFF), Colors.transparent], // White
  303. animationLayer3: [
  304. Color(0x0DB0C4DE),
  305. Colors.transparent,
  306. ], // Light steel blue
  307. zenModeColors: [Color(0x4D4169E1), Colors.transparent], // Royal blue
  308. );
  309. }