Added Google Drive storage location

This commit is contained in:
Courtney Arnold 2026-08-08 14:59:18 -05:00
parent e630f2a3ec
commit 73a7c93e64
22 changed files with 1690 additions and 205 deletions

View file

@ -1,8 +1,9 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import '../services/app_state.dart';
import 'drive_folder_browser_screen.dart';
class SettingsScreen extends StatefulWidget {
const SettingsScreen({super.key});
@ -12,31 +13,50 @@ class SettingsScreen extends StatefulWidget {
}
class _SettingsScreenState extends State<SettingsScreen> {
bool _changing = false;
bool _busy = false;
String? _error;
Future<void> _chooseFolder() async {
setState(() => _error = null);
final selectedPath = await FilePicker.platform.getDirectoryPath(
dialogTitle: 'Choose a folder for receipts and data',
);
if (selectedPath == null || !mounted) return;
setState(() => _changing = true);
Future<void> _connect() async {
setState(() {
_busy = true;
_error = null;
});
try {
await context.read<AppState>().changeSaveDirectory(selectedPath);
await context.read<AppState>().connectDrive();
} catch (e) {
setState(() => _error = 'Could not switch to that folder: $e');
setState(() => _error = 'Could not sign in: $e');
} finally {
if (mounted) setState(() => _changing = false);
if (mounted) setState(() => _busy = false);
}
}
Future<void> _disconnect() async {
setState(() => _busy = true);
try {
await context.read<AppState>().disconnectDrive();
} finally {
if (mounted) setState(() => _busy = false);
}
}
void _chooseFolder() {
Navigator.of(context).push(
MaterialPageRoute(builder: (_) => const DriveFolderBrowserScreen()),
);
}
Future<void> _syncNow() async {
setState(() => _busy = true);
try {
await context.read<AppState>().syncNow();
} finally {
if (mounted) setState(() => _busy = false);
}
}
@override
Widget build(BuildContext context) {
final appState = context.watch<AppState>();
final saveDir = appState.storage.saveDirectory.path;
return Scaffold(
appBar: AppBar(title: const Text('Settings')),
@ -49,35 +69,79 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Save Location', style: Theme.of(context).textTheme.titleMedium),
Text('Google Drive', style: Theme.of(context).textTheme.titleMedium),
const SizedBox(height: 8),
Text(
'Receipt photos and the data file are stored here:',
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 4),
SelectableText(
saveDir,
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontFamily: 'monospace',
if (!appState.isDriveConnected) ...[
Text(
'Connect Google Drive to share vehicles and fuel receipts with '
'other people, and to keep a backup off this device.',
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 12),
if (_error != null) ...[
Text(_error!, style: TextStyle(color: Theme.of(context).colorScheme.error)),
const SizedBox(height: 8),
],
FilledButton.icon(
onPressed: _busy ? null : _connect,
icon: const Icon(Icons.login),
label: const Text('Connect Google Drive'),
),
] else ...[
Text('Signed in as ${appState.driveAccountEmail}'),
const SizedBox(height: 4),
Text(
appState.driveFolderPath == null
? 'No folder selected yet.'
: 'Folder: ${appState.driveFolderPath}',
style: Theme.of(context).textTheme.bodySmall,
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
OutlinedButton.icon(
onPressed: _busy ? null : _chooseFolder,
icon: const Icon(Icons.folder_open),
label: Text(appState.driveFolderPath == null
? 'Choose Folder'
: 'Change Folder'),
),
),
const SizedBox(height: 16),
if (_error != null) ...[
Text(_error!, style: TextStyle(color: Theme.of(context).colorScheme.error)),
OutlinedButton.icon(
onPressed: (_busy || appState.driveFolderPath == null)
? null
: _syncNow,
icon: appState.isSyncing
? const SizedBox(
height: 16,
width: 16,
child: CircularProgressIndicator(strokeWidth: 2))
: const Icon(Icons.sync),
label: const Text('Sync Now'),
),
TextButton.icon(
onPressed: _busy ? null : _disconnect,
icon: const Icon(Icons.logout),
label: const Text('Disconnect'),
),
],
),
const SizedBox(height: 8),
if (appState.lastSyncedAt != null)
Text(
'Last synced ${DateFormat.yMMMd().add_jm().format(appState.lastSyncedAt!)}',
style: Theme.of(context).textTheme.bodySmall,
),
if (appState.lastSyncError != null)
Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(
'Last sync failed: ${appState.lastSyncError}',
style: TextStyle(color: Theme.of(context).colorScheme.error),
),
),
],
FilledButton.icon(
onPressed: _changing ? null : _chooseFolder,
icon: _changing
? const SizedBox(
height: 16,
width: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.folder_open),
label: const Text('Choose Folder'),
),
],
),
),