Files
receipity/lib/widgets/settings_button.dart
T
2026-08-31 16:51:31 +02:00

190 lines
5.8 KiB
Dart

/// 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<void> showSettingsSheet(BuildContext context) {
return showModalBottomSheet<void>(
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<void> _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<void> _import(AppSettings settings) async {
final confirmed = await showDialog<bool>(
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(),
],
],
),
);
},
);
}
}