main_menu.dart 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter/services.dart';
  3. import '../l10n/app_localizations.dart';
  4. import '../utils/colors.dart';
  5. import '../utils/haptic_utils.dart';
  6. import '../utils/settings_manager.dart';
  7. import '../utils/score_manager.dart';
  8. import '../utils/theme_notifier.dart';
  9. import '../game/audio/audio_manager.dart';
  10. import 'game_screen.dart';
  11. import 'settings_screen.dart';
  12. import 'stats_screen.dart';
  13. import 'components/animated_background.dart';
  14. import 'components/tutorial_overlay.dart';
  15. class MainMenu extends StatefulWidget {
  16. const MainMenu({super.key});
  17. @override
  18. State<MainMenu> createState() => _MainMenuState();
  19. }
  20. class _MainMenuState extends State<MainMenu> {
  21. bool _showTutorial = false;
  22. int _todayScore = 0;
  23. @override
  24. void initState() {
  25. super.initState();
  26. _checkTutorial();
  27. _initializeAndLoadScore();
  28. // Start menu music when entering main menu
  29. WidgetsBinding.instance.addPostFrameCallback((_) {
  30. AudioManager().playMenuMusic();
  31. });
  32. }
  33. @override
  34. void didChangeDependencies() {
  35. super.didChangeDependencies();
  36. // Refresh score when orientation changes or when returning from other screens
  37. _loadTodayScore();
  38. }
  39. void _initializeAndLoadScore() async {
  40. // Initialize score manager first
  41. await ScoreManager.initialize();
  42. _loadTodayScore();
  43. }
  44. void _loadTodayScore() {
  45. setState(() {
  46. _todayScore = ScoreManager.todayScore;
  47. });
  48. }
  49. void _checkTutorial() {
  50. WidgetsBinding.instance.addPostFrameCallback((_) {
  51. if (!SettingsManager.isTutorialShown) {
  52. setState(() {
  53. _showTutorial = true;
  54. });
  55. }
  56. });
  57. }
  58. @override
  59. Widget build(BuildContext context) {
  60. return ThemeAwareBuilder(
  61. builder: (context, theme) {
  62. return Scaffold(
  63. backgroundColor: ZenColors.currentAppBackground,
  64. body: OrientationBuilder(
  65. builder: (context, orientation) {
  66. return AnimatedBackground(
  67. child: Stack(
  68. children: [
  69. SafeArea(
  70. child: Padding(
  71. padding: const EdgeInsets.all(20.0),
  72. child:
  73. orientation == Orientation.portrait
  74. ? _buildPortraitLayout()
  75. : _buildLandscapeLayout(),
  76. ),
  77. ),
  78. // Tutorial overlay
  79. if (_showTutorial)
  80. TutorialOverlay(
  81. onComplete: () {
  82. setState(() {
  83. _showTutorial = false;
  84. });
  85. },
  86. ),
  87. ],
  88. ),
  89. );
  90. },
  91. ),
  92. );
  93. },
  94. );
  95. }
  96. Widget _buildPortraitLayout() {
  97. return Column(
  98. children: [
  99. // Main content
  100. Expanded(
  101. child: SingleChildScrollView(
  102. child: Column(
  103. mainAxisAlignment: MainAxisAlignment.center,
  104. children: [
  105. const SizedBox(height: 20),
  106. _buildGameTitle(),
  107. const SizedBox(height: 40),
  108. _buildTodayScore(),
  109. const SizedBox(height: 40),
  110. _buildMenuButtons(),
  111. const SizedBox(height: 40),
  112. _buildSettingsHint(),
  113. const SizedBox(height: 20),
  114. ],
  115. ),
  116. ),
  117. ),
  118. ],
  119. );
  120. }
  121. Widget _buildLandscapeLayout() {
  122. return Row(
  123. children: [
  124. // Left side - Title and score
  125. Expanded(
  126. flex: 2,
  127. child: Column(
  128. mainAxisAlignment: MainAxisAlignment.center,
  129. children: [
  130. _buildGameTitle(),
  131. const SizedBox(height: 20),
  132. _buildTodayScore(),
  133. const SizedBox(height: 20),
  134. _buildSettingsHint(),
  135. ],
  136. ),
  137. ),
  138. // Right side - Menu buttons
  139. Expanded(
  140. flex: 3,
  141. child: SingleChildScrollView(
  142. child: Column(
  143. mainAxisAlignment: MainAxisAlignment.center,
  144. children: [
  145. const SizedBox(height: 20),
  146. _buildResponsiveMenuButtons(),
  147. const SizedBox(height: 20),
  148. ],
  149. ),
  150. ),
  151. ),
  152. ],
  153. );
  154. }
  155. Widget _buildGameTitle() {
  156. return Column(
  157. children: [
  158. // Game Title
  159. Text(
  160. 'ZenTap',
  161. style: TextStyle(
  162. color: ZenColors.currentPrimaryText,
  163. fontSize: 48,
  164. fontWeight: FontWeight.bold,
  165. letterSpacing: 2.0,
  166. ),
  167. ),
  168. const SizedBox(height: 10),
  169. // Subtitle
  170. Text(
  171. AppLocalizations.of(context)!.appSubtitle,
  172. style: TextStyle(
  173. color: ZenColors.currentSecondaryText,
  174. fontSize: 18,
  175. fontStyle: FontStyle.italic,
  176. ),
  177. ),
  178. ],
  179. );
  180. }
  181. Widget _buildTodayScore() {
  182. return Container(
  183. padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
  184. decoration: BoxDecoration(
  185. color: ZenColors.black.withValues(alpha: 0.3),
  186. borderRadius: BorderRadius.circular(15),
  187. border: Border.all(
  188. color: ZenColors.currentButtonBackground.withValues(alpha: 0.5),
  189. width: 1,
  190. ),
  191. ),
  192. child: Text(
  193. AppLocalizations.of(context)!.todayRelaxationPoints(_todayScore),
  194. style: TextStyle(
  195. color: ZenColors.currentPrimaryText,
  196. fontSize: 18,
  197. fontWeight: FontWeight.w600,
  198. ),
  199. ),
  200. );
  201. }
  202. Widget _buildMenuButtons() {
  203. final l10n = AppLocalizations.of(context)!;
  204. return Column(
  205. children: [
  206. // Play Button
  207. _buildMenuButton(
  208. context,
  209. l10n.play,
  210. l10n.playDescription,
  211. Icons.play_arrow,
  212. () => _navigateToGame(context, false),
  213. ),
  214. const SizedBox(height: 20),
  215. // Zen Mode Button
  216. _buildMenuButton(
  217. context,
  218. l10n.zenMode,
  219. l10n.zenModeDescription,
  220. Icons.self_improvement,
  221. () => _navigateToGame(context, true),
  222. ),
  223. const SizedBox(height: 20),
  224. // Stats Button
  225. _buildMenuButton(
  226. context,
  227. l10n.stats,
  228. l10n.statisticsDescription,
  229. Icons.analytics,
  230. _openStats,
  231. ),
  232. const SizedBox(height: 20),
  233. // Settings Button
  234. _buildMenuButton(
  235. context,
  236. l10n.settings,
  237. 'Customize your experience',
  238. Icons.settings,
  239. _openSettings,
  240. ),
  241. const SizedBox(height: 20),
  242. // Exit Button
  243. _buildExitButton(
  244. context,
  245. l10n.exit,
  246. l10n.exitDescription,
  247. Icons.exit_to_app,
  248. _exitApp,
  249. ),
  250. ],
  251. );
  252. }
  253. Widget _buildSettingsHint() {
  254. return Text(
  255. AppLocalizations.of(context)!.tapToFeelCalm,
  256. style: TextStyle(color: ZenColors.currentMutedText, fontSize: 14),
  257. );
  258. }
  259. Widget _buildResponsiveMenuButtons() {
  260. return OrientationBuilder(
  261. builder: (context, orientation) {
  262. final buttonSpacing = orientation == Orientation.portrait ? 20.0 : 12.0;
  263. final l10n = AppLocalizations.of(context)!;
  264. return Column(
  265. children: [
  266. // Play Button
  267. _buildMenuButton(
  268. context,
  269. l10n.play,
  270. l10n.playDescription,
  271. Icons.play_arrow,
  272. () => _navigateToGame(context, false),
  273. ),
  274. SizedBox(height: buttonSpacing),
  275. // Zen Mode Button
  276. _buildMenuButton(
  277. context,
  278. l10n.zenMode,
  279. l10n.zenModeDescription,
  280. Icons.self_improvement,
  281. () => _navigateToGame(context, true),
  282. ),
  283. SizedBox(height: buttonSpacing),
  284. // Stats Button
  285. _buildMenuButton(
  286. context,
  287. l10n.stats,
  288. l10n.statisticsDescription,
  289. Icons.analytics,
  290. _openStats,
  291. ),
  292. SizedBox(height: buttonSpacing),
  293. // Settings Button
  294. _buildMenuButton(
  295. context,
  296. l10n.settings,
  297. 'Customize your experience',
  298. Icons.settings,
  299. _openSettings,
  300. ),
  301. SizedBox(height: buttonSpacing),
  302. // Exit Button
  303. _buildExitButton(
  304. context,
  305. l10n.exit,
  306. l10n.exitDescription,
  307. Icons.exit_to_app,
  308. _exitApp,
  309. ),
  310. ],
  311. );
  312. },
  313. );
  314. }
  315. Widget _buildMenuButton(
  316. BuildContext context,
  317. String title,
  318. String subtitle,
  319. IconData icon,
  320. VoidCallback onPressed,
  321. ) {
  322. return Container(
  323. width: double.infinity,
  324. margin: const EdgeInsets.symmetric(horizontal: 20),
  325. child: ElevatedButton(
  326. onPressed: onPressed,
  327. style: ElevatedButton.styleFrom(
  328. backgroundColor: ZenColors.currentButtonBackground,
  329. foregroundColor: ZenColors.currentButtonText,
  330. padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 30),
  331. shape: RoundedRectangleBorder(
  332. borderRadius: BorderRadius.circular(15),
  333. ),
  334. elevation: 8,
  335. ),
  336. child: Row(
  337. children: [
  338. Icon(icon, size: 32, color: ZenColors.currentButtonText),
  339. const SizedBox(width: 20),
  340. Expanded(
  341. child: Column(
  342. crossAxisAlignment: CrossAxisAlignment.start,
  343. children: [
  344. Text(
  345. title,
  346. style: TextStyle(
  347. fontSize: 22,
  348. fontWeight: FontWeight.bold,
  349. color: ZenColors.currentButtonText,
  350. ),
  351. ),
  352. const SizedBox(height: 4),
  353. Text(
  354. subtitle,
  355. style: TextStyle(
  356. fontSize: 14,
  357. color: ZenColors.currentButtonText.withValues(alpha: 0.8),
  358. ),
  359. ),
  360. ],
  361. ),
  362. ),
  363. Icon(
  364. Icons.arrow_forward_ios,
  365. color: ZenColors.currentButtonText.withValues(alpha: 0.7),
  366. size: 20,
  367. ),
  368. ],
  369. ),
  370. ),
  371. );
  372. }
  373. Widget _buildExitButton(
  374. BuildContext context,
  375. String title,
  376. String subtitle,
  377. IconData icon,
  378. VoidCallback onPressed,
  379. ) {
  380. return Container(
  381. width: double.infinity,
  382. margin: const EdgeInsets.symmetric(horizontal: 20),
  383. child: ElevatedButton(
  384. onPressed: onPressed,
  385. style: ElevatedButton.styleFrom(
  386. backgroundColor: ZenColors.red.withValues(alpha: 0.8),
  387. foregroundColor: ZenColors.white,
  388. padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 30),
  389. shape: RoundedRectangleBorder(
  390. borderRadius: BorderRadius.circular(15),
  391. ),
  392. elevation: 8,
  393. ),
  394. child: Row(
  395. children: [
  396. Icon(icon, size: 32, color: ZenColors.white),
  397. const SizedBox(width: 20),
  398. Expanded(
  399. child: Column(
  400. crossAxisAlignment: CrossAxisAlignment.start,
  401. children: [
  402. Text(
  403. title,
  404. style: const TextStyle(
  405. fontSize: 22,
  406. fontWeight: FontWeight.bold,
  407. color: ZenColors.white,
  408. ),
  409. ),
  410. const SizedBox(height: 4),
  411. Text(
  412. subtitle,
  413. style: TextStyle(
  414. fontSize: 14,
  415. color: ZenColors.white.withValues(alpha: 0.8),
  416. ),
  417. ),
  418. ],
  419. ),
  420. ),
  421. Icon(
  422. Icons.arrow_forward_ios,
  423. color: ZenColors.white.withValues(alpha: 0.7),
  424. size: 20,
  425. ),
  426. ],
  427. ),
  428. ),
  429. );
  430. }
  431. void _exitApp() async {
  432. if (SettingsManager.isHapticsEnabled) {
  433. await HapticUtils.vibrate(duration: 50);
  434. }
  435. // Close the app
  436. SystemNavigator.pop();
  437. }
  438. void _openSettings() async {
  439. if (SettingsManager.isHapticsEnabled) {
  440. await HapticUtils.vibrate(duration: 50);
  441. }
  442. if (!mounted) return;
  443. Navigator.of(
  444. context,
  445. ).push(MaterialPageRoute(builder: (context) => const SettingsScreen()));
  446. }
  447. void _openStats() async {
  448. if (SettingsManager.isHapticsEnabled) {
  449. await HapticUtils.vibrate(duration: 50);
  450. }
  451. if (!mounted) return;
  452. final result = await Navigator.of(
  453. context,
  454. ).push(MaterialPageRoute(builder: (context) => const StatsScreen()));
  455. // Refresh score when returning from stats
  456. if (result == true) {
  457. _loadTodayScore();
  458. }
  459. }
  460. void _navigateToGame(BuildContext ctx, bool isZenMode) async {
  461. if (SettingsManager.isHapticsEnabled) {
  462. await HapticUtils.vibrate(duration: 50);
  463. }
  464. if (!mounted) return;
  465. final result = await Navigator.of(context).push(
  466. MaterialPageRoute(builder: (context) => GameScreen(isZenMode: isZenMode)),
  467. );
  468. // Refresh score when returning from game
  469. if (mounted && result == true) {
  470. _loadTodayScore();
  471. }
  472. }
  473. }