main_menu.dart 12 KB

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