/// Gear that opens the settings sheet. library; import 'package:flutter/material.dart'; import '../services/app_settings.dart'; import '../services/backup_service.dart'; import '../supermarkets_screen.dart'; class SettingsButton extends StatelessWidget { const SettingsButton({super.key}); @override Widget build(BuildContext context) { return IconButton( tooltip: 'Settings', onPressed: () => showSettingsSheet(context), icon: const Icon(Icons.settings_outlined), ); } } Future showSettingsSheet(BuildContext context) { return showModalBottomSheet( context: context, isScrollControlled: true, showDragHandle: true, builder: (context) { return const _SettingsSheet(); }, ); } class _SettingsSheet extends StatefulWidget { const _SettingsSheet(); @override State<_SettingsSheet> createState() => _SettingsSheetState(); } class _SettingsSheetState extends State<_SettingsSheet> { bool _busy = false; Future _export(AppSettings settings) async { setState(() => _busy = true); try { final shared = await BackupService(settings.repository).exportBackup(); if (!mounted) return; if (shared) { ScaffoldMessenger.of(context) .showSnackBar(const SnackBar(content: Text('Backup ready.'))); } } catch (_) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Could not export the backup.')), ); } finally { if (mounted) setState(() => _busy = false); } } Future _import(AppSettings settings) async { final confirmed = await showDialog( context: context, builder: (context) { return AlertDialog( title: const Text('Import backup?'), content: const Text( 'This replaces all products, shopping trips, photos, and settings on this device.', ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('Cancel'), ), FilledButton( onPressed: () => Navigator.pop(context, true), child: const Text('Import'), ), ], ); }, ); if (confirmed != true || !mounted) return; setState(() => _busy = true); try { final imported = await BackupService(settings.repository).importBackup(); if (!mounted) return; if (imported) { await settings.reload(); if (!mounted) return; ScaffoldMessenger.of(context) .showSnackBar(const SnackBar(content: Text('Backup imported.'))); } } catch (_) { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Could not import this backup.')), ); } finally { if (mounted) setState(() => _busy = false); } } void _openSupermarkets(BuildContext context, AppSettings settings) { Navigator.of(context) ..pop() ..push( MaterialPageRoute( builder: (_) => SupermarketsScreen(repository: settings.repository), ), ); } @override Widget build(BuildContext context) { final settings = SettingsScope.of(context); final bottom = MediaQuery.viewInsetsOf(context).bottom; return ListenableBuilder( listenable: settings, builder: (context, _) { return Padding( padding: EdgeInsets.fromLTRB(16, 0, 16, 16 + bottom), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text('Settings', style: Theme.of(context).textTheme.titleLarge), const SizedBox(height: 8), SwitchListTile( contentPadding: EdgeInsets.zero, title: const Text('Dark mode'), value: settings.darkMode, onChanged: _busy ? null : settings.setDarkMode, ), ListTile( contentPadding: EdgeInsets.zero, leading: const Icon(Icons.store_outlined), title: const Text('Supermarkets'), subtitle: Text( settings.supermarkets.isEmpty ? 'Names, colors, and bonus cards' : '${settings.supermarkets.length} stores', ), trailing: const Icon(Icons.chevron_right), onTap: _busy ? null : () => _openSupermarkets(context, settings), ), const SizedBox(height: 16), Text('Backup', style: Theme.of(context).textTheme.titleMedium), Text( 'Save everything to a JSON file, or replace this device from a backup.', style: Theme.of(context).textTheme.bodyMedium, ), const SizedBox(height: 8), Row( children: [ Expanded( child: OutlinedButton.icon( onPressed: _busy ? null : () => _export(settings), icon: const Icon(Icons.file_upload_outlined), label: const Text('Export'), ), ), const SizedBox(width: 8), Expanded( child: OutlinedButton.icon( onPressed: _busy ? null : () => _import(settings), icon: const Icon(Icons.file_download_outlined), label: const Text('Import'), ), ), ], ), if (_busy) ...[ const SizedBox(height: 8), const LinearProgressIndicator(), ], ], ), ); }, ); } }