/// Settings Page library; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:package_info_plus/package_info_plus.dart'; import '../../../core/theme/colors.dart'; import '../../../core/theme/text_styles.dart'; import '../../providers/settings_provider.dart'; import '../../providers/auth_provider.dart'; import '../../widgets/settings/profile_section.dart'; import '../../widgets/settings/audio_quality_selector.dart'; import '../../widgets/settings/cache_management_tile.dart'; import '../../widgets/settings/settings_tile.dart'; /// Settings page class SettingsPage extends ConsumerStatefulWidget { const SettingsPage({super.key}); @override ConsumerState createState() => _SettingsPageState(); } class _SettingsPageState extends ConsumerState { String _appVersion = '1.0.0'; @override void initState() { super.initState(); _loadAppVersion(); // Load settings on init WidgetsBinding.instance.addPostFrameCallback((_) { ref.read(settingsProvider.notifier).loadSettings(); }); } Future _loadAppVersion() async { final info = await PackageInfo.fromPlatform(); setState(() { _appVersion = '${info.version}+${info.buildNumber}'; }); } @override Widget build(BuildContext context) { final settingsState = ref.watch(settingsProvider); final authState = ref.watch(authProvider); return Scaffold( body: CustomScrollView( slivers: [ // App Bar SliverAppBar( floating: true, pinned: true, elevation: 0, backgroundColor: AppColors.primary.withOpacity(0.8), leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new_rounded), onPressed: () => Navigator.pop(context), color: AppColors.onBackground, ), title: Text( 'Settings', style: AppTextStyles.h2.copyWith( color: AppColors.onBackground, fontWeight: FontWeight.w700, ), ), ), // Content SliverToBoxAdapter( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 8), // Profile Section const ProfileSection(), const SizedBox(height: 24), // Audio Quality Section const SettingsSectionHeader(title: 'Audio'), const AudioQualitySelector(), // Playback Section const SettingsSectionHeader(title: 'Playback'), SettingsCard( children: [ SettingsToggleTile( title: 'Crossfade', subtitle: 'Smooth transition between tracks', leading: const Icon(Icons.fade_out), value: settingsState.crossfadeEnabled, onChanged: (value) { ref .read(settingsProvider.notifier) .toggleCrossfade(value); }, ), if (settingsState.crossfadeEnabled) Padding( padding: const EdgeInsets.symmetric( horizontal: 16, vertical: 8, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Crossfade Duration: ${settingsState.crossfadeDuration.toInt()}s', style: AppTextStyles.bodySmall.copyWith( color: AppColors.muted, ), ), Slider( value: settingsState.crossfadeDuration, min: 1, max: 12, divisions: 11, activeColor: AppColors.cyan, onChanged: (value) { ref .read(settingsProvider.notifier) .setCrossfadeDuration(value); }, ), ], ), ), const Divider(height: 1, color: AppColors.surfaceVariant), SettingsToggleTile( title: 'Gapless Playback', subtitle: 'No gap between tracks', leading: const Icon(Icons.all_inclusive), value: settingsState.gaplessPlayback, onChanged: (value) { ref .read(settingsProvider.notifier) .toggleGaplessPlayback(value); }, ), const Divider(height: 1, color: AppColors.surfaceVariant), SettingsToggleTile( title: 'Normalize Volume', subtitle: 'Set same volume level for all tracks', leading: const Icon(Icons.volume_up), value: settingsState.normalizeVolume, onChanged: (value) { ref .read(settingsProvider.notifier) .toggleNormalizeVolume(value); }, ), ], ), // Downloads Section const SettingsSectionHeader(title: 'Downloads'), SettingsCard( children: [ SettingsToggleTile( title: 'Download on Mobile Data', subtitle: 'May use extra data', leading: const Icon(Icons.download_done), value: settingsState.downloadOnMobileData, onChanged: (value) { ref .read(settingsProvider.notifier) .toggleDownloadOnMobileData(value); }, ), const Divider(height: 1, color: AppColors.surfaceVariant), SettingsToggleTile( title: 'Show Explicit Content', subtitle: 'Display explicit content in search', leading: const Icon(Icons.explicit), value: settingsState.showExplicitContent, onChanged: (value) { ref .read(settingsProvider.notifier) .toggleShowExplicitContent(value); }, ), ], ), const SizedBox(height: 8), // Cache Management const CacheManagementTile(), const SizedBox(height: 24), // About Section const SettingsSectionHeader(title: 'About'), SettingsCard( children: [ SettingsTile( title: 'App Version', subtitle: _appVersion, leading: const Icon(Icons.info_outline), ), const Divider(height: 1, color: AppColors.surfaceVariant), SettingsTile( title: 'Licenses', subtitle: 'Open source licenses', leading: const Icon(Icons.description_outlined), onTap: () { showLicensePage( context: context, applicationName: 'Spotify Le 2', applicationVersion: _appVersion, applicationLegalese: '© 2025 Spotify Le 2', ); }, ), ], ), const SizedBox(height: 24), // Logout Button Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: SizedBox( width: double.infinity, child: OutlinedButton.icon( onPressed: () => _showLogoutDialog(context, ref), icon: const Icon(Icons.logout, size: 18), label: const Text('Log Out'), style: OutlinedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 14), side: BorderSide( color: AppColors.rose.withOpacity(0.5), width: 1.5, ), foregroundColor: AppColors.rose, ), ), ), ), const SizedBox(height: 32), // Error message if (settingsState.error != null) Container( margin: const EdgeInsets.symmetric(horizontal: 16), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppColors.error.withOpacity(0.1), borderRadius: BorderRadius.circular(12), border: Border.all( color: AppColors.error.withOpacity(0.3), ), ), child: Row( children: [ const Icon( Icons.error_outline, color: AppColors.error, ), const SizedBox(width: 12), Expanded( child: Text( settingsState.error!, style: AppTextStyles.bodySmall.copyWith( color: AppColors.error, ), ), ), IconButton( onPressed: () { ref .read(settingsProvider.notifier) .copyWith(error: null); }, icon: const Icon(Icons.close, size: 20), color: AppColors.error, ), ], ), ), const SizedBox(height: 24), ], ), ), ], ), ); } void _showLogoutDialog(BuildContext context, WidgetRef ref) { showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: AppColors.surface, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), side: BorderSide( color: AppColors.cyan.withOpacity(0.2), width: 1, ), ), title: Row( children: [ Container( width: 36, height: 36, decoration: BoxDecoration( color: AppColors.rose.withOpacity(0.1), borderRadius: BorderRadius.circular(8), ), child: const Icon( Icons.logout, color: AppColors.rose, size: 20, ), ), const SizedBox(width: 12), Text( 'Log Out', style: AppTextStyles.h3.copyWith( color: AppColors.onBackground, ), ), ], ), content: Text( 'Are you sure you want to log out?', style: AppTextStyles.body.copyWith( color: AppColors.onSurface, ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( 'Cancel', style: AppTextStyles.button.copyWith( color: AppColors.muted, ), ), ), ElevatedButton( onPressed: () async { Navigator.pop(context); await ref.read(authProvider.notifier).logout(); if (context.mounted) { Navigator.of(context).pushNamedAndRemoveUntil( '/login', (route) => false, ); } }, style: ElevatedButton.styleFrom( backgroundColor: AppColors.rose, foregroundColor: Colors.white, ), child: Text( 'Log Out', style: AppTextStyles.button, ), ), ], ), ); } }