|
|
@@ -0,0 +1,417 @@
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+import 'package:vibration/vibration.dart';
|
|
|
+import '../utils/colors.dart';
|
|
|
+import '../utils/settings_manager.dart';
|
|
|
+import 'components/animated_background.dart';
|
|
|
+
|
|
|
+class SettingsScreen extends StatefulWidget {
|
|
|
+ const SettingsScreen({super.key});
|
|
|
+
|
|
|
+ @override
|
|
|
+ State<SettingsScreen> createState() => _SettingsScreenState();
|
|
|
+}
|
|
|
+
|
|
|
+class _SettingsScreenState extends State<SettingsScreen> {
|
|
|
+ bool _musicEnabled = true;
|
|
|
+ bool _hapticsEnabled = true;
|
|
|
+
|
|
|
+ @override
|
|
|
+ void initState() {
|
|
|
+ super.initState();
|
|
|
+ _loadSettings();
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<void> _loadSettings() async {
|
|
|
+ setState(() {
|
|
|
+ _musicEnabled = SettingsManager.isMusicEnabled;
|
|
|
+ _hapticsEnabled = SettingsManager.isHapticsEnabled;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ return Scaffold(
|
|
|
+ backgroundColor: ZenColors.appBackground,
|
|
|
+ body: AnimatedBackground(
|
|
|
+ child: SafeArea(
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ // Header
|
|
|
+ Padding(
|
|
|
+ padding: const EdgeInsets.all(16.0),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ IconButton(
|
|
|
+ onPressed: () => Navigator.of(context).pop(),
|
|
|
+ icon: const Icon(
|
|
|
+ Icons.arrow_back,
|
|
|
+ color: ZenColors.primaryText,
|
|
|
+ size: 28,
|
|
|
+ ),
|
|
|
+ style: IconButton.styleFrom(
|
|
|
+ backgroundColor: ZenColors.black.withOpacity(0.3),
|
|
|
+ shape: const CircleBorder(),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 16),
|
|
|
+ const Text(
|
|
|
+ 'Settings',
|
|
|
+ style: TextStyle(
|
|
|
+ color: ZenColors.primaryText,
|
|
|
+ fontSize: 28,
|
|
|
+ fontWeight: FontWeight.bold,
|
|
|
+ letterSpacing: 1.0,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+
|
|
|
+ // Settings List
|
|
|
+ Expanded(
|
|
|
+ child: Padding(
|
|
|
+ padding: const EdgeInsets.symmetric(horizontal: 20.0),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ const SizedBox(height: 20),
|
|
|
+
|
|
|
+ // Audio Section
|
|
|
+ _buildSectionHeader('Audio'),
|
|
|
+ _buildSettingTile(
|
|
|
+ icon: Icons.music_note,
|
|
|
+ title: 'Background Music',
|
|
|
+ subtitle: 'Enable relaxing ambient sounds',
|
|
|
+ value: _musicEnabled,
|
|
|
+ onChanged: _toggleMusic,
|
|
|
+ ),
|
|
|
+
|
|
|
+ const SizedBox(height: 30),
|
|
|
+
|
|
|
+ // Feedback Section
|
|
|
+ _buildSectionHeader('Feedback'),
|
|
|
+ _buildSettingTile(
|
|
|
+ icon: Icons.vibration,
|
|
|
+ title: 'Haptic Feedback',
|
|
|
+ subtitle: 'Feel gentle vibrations on tap',
|
|
|
+ value: _hapticsEnabled,
|
|
|
+ onChanged: _toggleHaptics,
|
|
|
+ ),
|
|
|
+
|
|
|
+ const SizedBox(height: 30),
|
|
|
+
|
|
|
+ // Tutorial Section
|
|
|
+ _buildSectionHeader('Help'),
|
|
|
+ _buildActionTile(
|
|
|
+ icon: Icons.help_outline,
|
|
|
+ title: 'Show Tutorial',
|
|
|
+ subtitle: 'Learn how to use ZenTap',
|
|
|
+ onTap: _showTutorial,
|
|
|
+ ),
|
|
|
+
|
|
|
+ const Spacer(),
|
|
|
+
|
|
|
+ // About Section
|
|
|
+ Padding(
|
|
|
+ padding: const EdgeInsets.only(bottom: 20),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ 'ZenTap v1.0.0',
|
|
|
+ style: TextStyle(
|
|
|
+ color: ZenColors.mutedText,
|
|
|
+ fontSize: 14,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 8),
|
|
|
+ Text(
|
|
|
+ 'A stress relief tapping game',
|
|
|
+ style: TextStyle(
|
|
|
+ color: ZenColors.mutedText,
|
|
|
+ fontSize: 12,
|
|
|
+ fontStyle: FontStyle.italic,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildSectionHeader(String title) {
|
|
|
+ return Align(
|
|
|
+ alignment: Alignment.centerLeft,
|
|
|
+ child: Text(
|
|
|
+ title,
|
|
|
+ style: TextStyle(
|
|
|
+ color: ZenColors.secondaryText,
|
|
|
+ fontSize: 16,
|
|
|
+ fontWeight: FontWeight.w600,
|
|
|
+ letterSpacing: 0.5,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildSettingTile({
|
|
|
+ required IconData icon,
|
|
|
+ required String title,
|
|
|
+ required String subtitle,
|
|
|
+ required bool value,
|
|
|
+ required ValueChanged<bool> onChanged,
|
|
|
+ }) {
|
|
|
+ return Container(
|
|
|
+ margin: const EdgeInsets.only(top: 12),
|
|
|
+ padding: const EdgeInsets.all(16),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: ZenColors.uiElements.withOpacity(0.3),
|
|
|
+ borderRadius: BorderRadius.circular(12),
|
|
|
+ border: Border.all(
|
|
|
+ color: ZenColors.uiElements.withOpacity(0.2),
|
|
|
+ width: 1,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ Icon(
|
|
|
+ icon,
|
|
|
+ color: ZenColors.primaryText,
|
|
|
+ size: 24,
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 16),
|
|
|
+ Expanded(
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ title,
|
|
|
+ style: const TextStyle(
|
|
|
+ color: ZenColors.primaryText,
|
|
|
+ fontSize: 16,
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 2),
|
|
|
+ Text(
|
|
|
+ subtitle,
|
|
|
+ style: TextStyle(
|
|
|
+ color: ZenColors.secondaryText,
|
|
|
+ fontSize: 13,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Switch(
|
|
|
+ value: value,
|
|
|
+ onChanged: onChanged,
|
|
|
+ activeColor: ZenColors.buttonBackground,
|
|
|
+ activeTrackColor: ZenColors.buttonBackground.withOpacity(0.3),
|
|
|
+ inactiveThumbColor: ZenColors.mutedText,
|
|
|
+ inactiveTrackColor: ZenColors.mutedText.withOpacity(0.2),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildActionTile({
|
|
|
+ required IconData icon,
|
|
|
+ required String title,
|
|
|
+ required String subtitle,
|
|
|
+ required VoidCallback onTap,
|
|
|
+ }) {
|
|
|
+ return Container(
|
|
|
+ margin: const EdgeInsets.only(top: 12),
|
|
|
+ child: Material(
|
|
|
+ color: ZenColors.uiElements.withOpacity(0.3),
|
|
|
+ borderRadius: BorderRadius.circular(12),
|
|
|
+ child: InkWell(
|
|
|
+ onTap: onTap,
|
|
|
+ borderRadius: BorderRadius.circular(12),
|
|
|
+ child: Container(
|
|
|
+ padding: const EdgeInsets.all(16),
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ borderRadius: BorderRadius.circular(12),
|
|
|
+ border: Border.all(
|
|
|
+ color: ZenColors.uiElements.withOpacity(0.2),
|
|
|
+ width: 1,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ children: [
|
|
|
+ Icon(
|
|
|
+ icon,
|
|
|
+ color: ZenColors.primaryText,
|
|
|
+ size: 24,
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 16),
|
|
|
+ Expanded(
|
|
|
+ child: Column(
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ Text(
|
|
|
+ title,
|
|
|
+ style: const TextStyle(
|
|
|
+ color: ZenColors.primaryText,
|
|
|
+ fontSize: 16,
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 2),
|
|
|
+ Text(
|
|
|
+ subtitle,
|
|
|
+ style: TextStyle(
|
|
|
+ color: ZenColors.secondaryText,
|
|
|
+ fontSize: 13,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ Icon(
|
|
|
+ Icons.arrow_forward_ios,
|
|
|
+ color: ZenColors.mutedText,
|
|
|
+ size: 16,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<void> _toggleMusic(bool value) async {
|
|
|
+ setState(() {
|
|
|
+ _musicEnabled = value;
|
|
|
+ });
|
|
|
+ await SettingsManager.setMusicEnabled(value);
|
|
|
+
|
|
|
+ if (_hapticsEnabled) {
|
|
|
+ try {
|
|
|
+ await Vibration.vibrate(duration: 50);
|
|
|
+ } catch (e) {
|
|
|
+ // Vibration not supported on this platform
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<void> _toggleHaptics(bool value) async {
|
|
|
+ setState(() {
|
|
|
+ _hapticsEnabled = value;
|
|
|
+ });
|
|
|
+ await SettingsManager.setHapticsEnabled(value);
|
|
|
+
|
|
|
+ // Give immediate feedback if enabling haptics
|
|
|
+ if (value) {
|
|
|
+ try {
|
|
|
+ await Vibration.vibrate(duration: 100);
|
|
|
+ } catch (e) {
|
|
|
+ // Vibration not supported on this platform
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ void _showTutorial() {
|
|
|
+ if (_hapticsEnabled) {
|
|
|
+ try {
|
|
|
+ Vibration.vibrate(duration: 50);
|
|
|
+ } catch (e) {
|
|
|
+ // Vibration not supported on this platform
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ showDialog(
|
|
|
+ context: context,
|
|
|
+ builder: (context) => _buildTutorialDialog(),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildTutorialDialog() {
|
|
|
+ return AlertDialog(
|
|
|
+ backgroundColor: ZenColors.uiElements,
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.circular(20),
|
|
|
+ ),
|
|
|
+ title: const Text(
|
|
|
+ 'How to Use ZenTap',
|
|
|
+ style: TextStyle(
|
|
|
+ color: ZenColors.primaryText,
|
|
|
+ fontSize: 22,
|
|
|
+ fontWeight: FontWeight.bold,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ content: Column(
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
+ children: [
|
|
|
+ _buildTutorialStep(
|
|
|
+ icon: Icons.touch_app,
|
|
|
+ text: 'Tap anywhere on the screen to pop bubbles',
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 16),
|
|
|
+ _buildTutorialStep(
|
|
|
+ icon: Icons.stars,
|
|
|
+ text: 'Earn Relaxation Points in Play mode',
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 16),
|
|
|
+ _buildTutorialStep(
|
|
|
+ icon: Icons.self_improvement,
|
|
|
+ text: 'Choose Zen Mode for pure relaxation',
|
|
|
+ ),
|
|
|
+ const SizedBox(height: 16),
|
|
|
+ _buildTutorialStep(
|
|
|
+ icon: Icons.pause,
|
|
|
+ text: 'Tap pause anytime to take a break',
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ actions: [
|
|
|
+ ElevatedButton(
|
|
|
+ onPressed: () => Navigator.of(context).pop(),
|
|
|
+ style: ElevatedButton.styleFrom(
|
|
|
+ backgroundColor: ZenColors.buttonBackground,
|
|
|
+ foregroundColor: ZenColors.buttonText,
|
|
|
+ shape: RoundedRectangleBorder(
|
|
|
+ borderRadius: BorderRadius.circular(12),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: const Text('Got it!'),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ Widget _buildTutorialStep({
|
|
|
+ required IconData icon,
|
|
|
+ required String text,
|
|
|
+ }) {
|
|
|
+ return Row(
|
|
|
+ children: [
|
|
|
+ Icon(
|
|
|
+ icon,
|
|
|
+ color: ZenColors.buttonBackground,
|
|
|
+ size: 20,
|
|
|
+ ),
|
|
|
+ const SizedBox(width: 12),
|
|
|
+ Expanded(
|
|
|
+ child: Text(
|
|
|
+ text,
|
|
|
+ style: TextStyle(
|
|
|
+ color: ZenColors.secondaryText,
|
|
|
+ fontSize: 14,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|