import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:provider/provider.dart'; import '../models/settings.dart'; import '../viewmodels/pin_lock_view_model.dart'; import '../viewmodels/settings_view_model.dart'; class SettingsScreenView extends StatefulWidget { const SettingsScreenView({super.key}); @override State createState() => _SettingsScreenViewState(); } class _SettingsScreenViewState extends State { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { context.read().ensureLoaded(); context.read().ensureLoaded(); }); } Future _promptPin(String title, {String hint = 'Enter PIN'}) { final controller = TextEditingController(); return showDialog( context: context, builder: (context) => AlertDialog( title: Text(title), content: TextField( controller: controller, autofocus: true, obscureText: true, keyboardType: TextInputType.number, maxLength: 6, decoration: InputDecoration(hintText: hint), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Cancel'), ), FilledButton( onPressed: () => Navigator.pop(context, controller.text), child: const Text('Confirm'), ), ], ), ); } void _showError(String message) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(message))); } Future _enablePin() async { final pin = await _promptPin('Set PIN Code', hint: '4–6 digits'); if (pin == null) return; if (pin.length < 4) { _showError('PIN must be at least 4 digits'); return; } final pinLockViewModel = context.read(); final success = await pinLockViewModel.setPin(pin); if (!success) { _showError(pinLockViewModel.errorMessage ?? 'Could not set PIN.'); return; } await context.read().updatePinRequired(true); } Future _changePin() async { final current = await _promptPin('Enter Current PIN'); if (current == null) return; final newPin = await _promptPin('Enter New PIN', hint: '4–6 digits'); if (newPin == null) return; if (newPin.length < 4) { _showError('PIN must be at least 4 digits'); return; } final pinLockViewModel = context.read(); final success = await pinLockViewModel.changePin( currentPin: current, newPin: newPin, ); if (!success) { _showError(pinLockViewModel.errorMessage ?? 'Could not change PIN.'); } } Future _disablePin() async { final current = await _promptPin('Enter Current PIN to Disable'); if (current == null) return; final pinLockViewModel = context.read(); final success = await pinLockViewModel.disablePin(current); if (!success) { _showError(pinLockViewModel.errorMessage ?? 'Could not disable PIN.'); return; } await context.read().updatePinRequired(false); } @override Widget build(BuildContext context) { final settingsViewModel = context.watch(); final pinLockViewModel = context.watch(); return Scaffold( appBar: AppBar( title: Row( children: [ IconButton( onPressed: () => context.go('/bar'), icon: const Icon(Icons.arrow_back), ), const SizedBox(width: 5), const Text('Settings'), ], ), ), body: Builder( builder: (context) { final isLoading = settingsViewModel.isLoading || pinLockViewModel.isLoading; final hasLoaded = settingsViewModel.hasLoaded && pinLockViewModel.hasLoaded; if (isLoading && !hasLoaded) { return const Center( child: CircularProgressIndicator(strokeWidth: 2.5), ); } final settings = settingsViewModel.settings; return ListView( padding: const EdgeInsets.symmetric(vertical: 12), children: [ _SettingsSection( title: 'Security', children: [ _SettingsSwitchTile( icon: Icons.lock_outline_rounded, title: 'PIN Required', value: settings.pinRequired, onChanged: (value) { if (value) { _enablePin(); } else { _disablePin(); } }, ), if (settings.pinRequired) _SettingsTile( icon: Icons.pin_rounded, title: 'Change PIN', onTap: _changePin, ), ], ), _SettingsSection( title: 'Appearance', children: [ _SettingsTile( icon: Icons.brightness_6_rounded, title: 'Theme', subtitle: switch (settings.themeMode) { AppThemeMode.system => 'System', AppThemeMode.light => 'Light', AppThemeMode.dark => 'Dark', }, onTap: () async { final selected = await showModalBottomSheet( context: context, builder: (context) => SafeArea( child: Column( mainAxisSize: MainAxisSize.min, children: AppThemeMode.values.map((mode) { return RadioListTile( value: mode, groupValue: settings.themeMode, title: Text(switch (mode) { AppThemeMode.system => 'System', AppThemeMode.light => 'Light', AppThemeMode.dark => 'Dark', }), onChanged: (value) => Navigator.pop(context, value), ); }).toList(), ), ), ); if (selected != null) { await settingsViewModel.updateThemeMode(selected); } }, ), ], ), ], ); }, ), ); } } class _SettingsSection extends StatelessWidget { final String title; final List children; const _SettingsSection({required this.title, required this.children}); @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; return Padding( padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title.toUpperCase(), style: TextStyle( fontSize: 12, fontWeight: FontWeight.w700, letterSpacing: 0.5, color: scheme.onSurface.withValues(alpha: 0.5), ), ), const SizedBox(height: 8), Container( decoration: BoxDecoration( color: scheme.onSurface.withValues(alpha: 0.04), borderRadius: BorderRadius.circular(14), border: Border.all(color: scheme.onSurface.withValues(alpha: 0.06)), ), clipBehavior: Clip.antiAlias, child: Column(children: children), ), ], ), ); } } class _SettingsTile extends StatelessWidget { final IconData icon; final String title; final String? subtitle; final VoidCallback? onTap; const _SettingsTile({ required this.icon, required this.title, this.subtitle, this.onTap, }); @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; return Material( color: Colors.transparent, child: InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), child: Row( children: [ Icon(icon, size: 22, color: scheme.onSurface.withValues(alpha: 0.7)), const SizedBox(width: 14), Expanded( child: Text( title, style: TextStyle(fontWeight: FontWeight.w600, color: scheme.onSurface), ), ), if (subtitle != null) ...[ Text(subtitle!, style: Theme.of(context).textTheme.bodySmall), const SizedBox(width: 4), ], if (onTap != null) Icon(Icons.chevron_right_rounded, color: scheme.onSurface.withValues(alpha: 0.3)), ], ), ), ), ); } } class _SettingsSwitchTile extends StatelessWidget { final IconData icon; final String title; final bool value; final ValueChanged onChanged; const _SettingsSwitchTile({ required this.icon, required this.title, required this.value, required this.onChanged, }); @override Widget build(BuildContext context) { final scheme = Theme.of(context).colorScheme; return Padding( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), child: Row( children: [ Icon(icon, size: 22, color: scheme.onSurface.withValues(alpha: 0.7)), const SizedBox(width: 14), Expanded( child: Text( title, style: TextStyle(fontWeight: FontWeight.w600, color: scheme.onSurface), ), ), Switch(value: value, onChanged: onChanged), ], ), ); } }