import 'dart:async'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:provider/provider.dart'; import '../l10n/app_localizations.dart'; import '../service/notifying.dart'; import '../service/permission_service.dart'; import '../service/settings_provider.dart'; import '../service/storage.dart'; class SettingsPage extends StatefulWidget { static const routeName = '/settings'; const SettingsPage({super.key}); @override State createState() => _SettingsPageState(); } class _SettingsPageState extends State { bool storagePermissionIsGranted = false; final tileSpacing = 16.0; @override void initState() { PermissionService.storagePermissionStatus.then((value) { storagePermissionIsGranted = value.isGranted; if (context.mounted) setState(() {}); }); super.initState(); } // TODO: Localize @override Widget build(BuildContext context) { final titlePadding = Theme.of(context).listTileTheme.contentPadding!; checkStoragePermission(); final alwaysExportEnabled = context .watch() .settings .alwaysExportEnabled; return Scaffold( appBar: AppBar(title: Text(AppLocalizations.of(context)!.settings)), body: Center( child: SizedBox( width: MediaQuery.of(context).size.width * 0.9, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: titlePadding, child: Text( AppLocalizations.of(context)!.appData, style: Theme.of(context).textTheme.titleLarge, ), ), SizedBox(height: tileSpacing), ListTile( title: Text('Grant storage permisson'), subtitle: storagePermissionIsGranted ? Text('Storage permission granted') : Text( 'For app-data settings to work, you need to grant the app permissions to manage internal storage.', ), onTap: () => PermissionService.requestStoragePermission, trailing: Icon(Icons.arrow_forward_ios_rounded), enabled: !storagePermissionIsGranted, ), SizedBox(height: tileSpacing), ListTile( title: Text(AppLocalizations.of(context)!.import), subtitle: Text('Import app-data from a json file.'), onTap: () => onJsonImportPressed(), trailing: Icon(Icons.arrow_forward_ios_rounded), enabled: storagePermissionIsGranted, ), SizedBox(height: tileSpacing), ListTile( title: Text(AppLocalizations.of(context)!.export), subtitle: Text( 'Export app-data to a json file in the selected directory.', ), onTap: () => onJsonExportPressed(), trailing: Icon(Icons.arrow_forward_ios_rounded), enabled: storagePermissionIsGranted, ), SizedBox(height: tileSpacing), ListTile( title: Text('Always save to file'), subtitle: Text( 'Export app data to a directory, every time you make a change', ), onTap: () => onAlwaysSaveToJsonPressed(), trailing: Checkbox( value: alwaysExportEnabled, onChanged: (value) { onAlwaysSaveToJsonPressed(); }, ), enabled: storagePermissionIsGranted, ), if (alwaysExportEnabled) SizedBox(height: tileSpacing), if (alwaysExportEnabled) ListTile( title: Text('Change export directory'), subtitle: Text( context .watch() .settings .exportDirectoryPath, ), onTap: () => onChangeExportDirectoryPressed(), trailing: Icon(Icons.arrow_forward_ios_rounded), enabled: storagePermissionIsGranted, ), ], ), ), ), ); } void onJsonExportPressed() async { if (!await checkStoragePermission()) return; Storage.exportToJsonFile().then(showExportInfo); } void onJsonImportPressed() async { if (!await checkStoragePermission()) return; Storage.importFromJsonFile().then(showImportInfo); } void onAlwaysSaveToJsonPressed() async { if (context.read().settings.alwaysExportEnabled) { context.read().setExportDirectoryPath('', silent: true); context.read().setAlwaysExportEnabled(false); return; } if (!await PermissionService.storagePermissionStatus.isGranted) return; final dir = await Storage.selectDirectoryPath(); if (dir.isEmpty || !context.mounted) return; // ignore: use_build_context_synchronously context.read().setExportDirectoryPath(dir, silent: true); // ignore: use_build_context_synchronously Storage.saveDataToFile().whenComplete( // ignore: use_build_context_synchronously () => context.read().setAlwaysExportEnabled(true), ); } void onChangeExportDirectoryPressed() async { if (!await PermissionService.storagePermissionStatus.isGranted) return; final dir = await Storage.selectDirectoryPath(); if (dir.isEmpty || !context.mounted) return; // ignore: use_build_context_synchronously context.read().setExportDirectoryPath(dir); } Future checkStoragePermission() async { PermissionService.storagePermissionStatus.then((value) { if (context.mounted && value.isGranted != storagePermissionIsGranted) { storagePermissionIsGranted = value.isGranted; setState(() {}); } }); return storagePermissionIsGranted; } void showExportInfo(bool success) => Notifying.showSnackbar( context, text: success ? AppLocalizations.of(context)!.exportSuccess : AppLocalizations.of(context)!.exportFailed, isError: !success, ); void showImportInfo(bool success) => Notifying.showSnackbar( context, text: success ? AppLocalizations.of(context)!.importSuccess : AppLocalizations.of(context)!.importFailed, isError: !success, ); }