import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../providers/offline_provider.dart'; import '../../data/offline_repository.dart'; class OfflineScreen extends ConsumerWidget { const OfflineScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final offlineState = ref.watch(offlineProvider); return Scaffold( appBar: AppBar( title: const Text('Offline Maps'), ), body: offlineState.isLoading ? const Center(child: CircularProgressIndicator()) : _buildContent(context, ref, offlineState), ); } Widget _buildContent( BuildContext context, WidgetRef ref, OfflineState offlineState, ) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Storage info Container( width: double.infinity, padding: const EdgeInsets.all(16), color: Theme.of(context).colorScheme.surfaceContainerHighest, child: Text( 'Storage used: ${offlineState.totalStorageUsedMb} MB', style: Theme.of(context).textTheme.bodyMedium, ), ), // Error if (offlineState.error != null) Container( width: double.infinity, padding: const EdgeInsets.all(12), color: Theme.of(context).colorScheme.errorContainer, child: Text( offlineState.error!, style: TextStyle( color: Theme.of(context).colorScheme.onErrorContainer, ), ), ), // Region list Expanded( child: offlineState.availableRegions.isEmpty ? const Center(child: Text('No regions available.')) : ListView.builder( itemCount: offlineState.availableRegions.length, itemBuilder: (context, index) { final region = offlineState.availableRegions[index]; return _RegionTile( region: region, isDownloaded: offlineState.isRegionDownloaded(region.id), isDownloading: offlineState.isRegionDownloading(region.id), progress: offlineState.downloadProgress[region.id], ); }, ), ), ], ); } } class _RegionTile extends ConsumerWidget { final OfflineRegionInfo region; final bool isDownloaded; final bool isDownloading; final DownloadProgress? progress; const _RegionTile({ required this.region, required this.isDownloaded, required this.isDownloading, this.progress, }); @override Widget build(BuildContext context, WidgetRef ref) { return Card( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 6), child: Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( region.name, style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 2), Text( region.description, style: Theme.of(context).textTheme.bodySmall, ), ], ), ), Text( '${region.sizeMb} MB', style: Theme.of(context).textTheme.labelLarge?.copyWith( color: Theme.of(context).colorScheme.primary, ), ), ], ), const SizedBox(height: 8), // Component size breakdown Wrap( spacing: 8, runSpacing: 4, children: [ _SizeChip( 'Tiles', region.components['tiles_mb'] ?? 0), _SizeChip( 'Drive', region.components['routing_driving_mb'] ?? 0), _SizeChip( 'Walk', region.components['routing_walking_mb'] ?? 0), _SizeChip( 'Cycle', region.components['routing_cycling_mb'] ?? 0), _SizeChip('POIs', region.components['pois_mb'] ?? 0), ], ), const SizedBox(height: 12), // Progress indicator if (isDownloading && progress != null) ...[ LinearProgressIndicator(value: progress!.fraction), const SizedBox(height: 4), Text( 'Downloading ${progress!.component}...', style: Theme.of(context).textTheme.bodySmall, ), ], // Action buttons if (!isDownloading) Row( mainAxisAlignment: MainAxisAlignment.end, children: [ if (isDownloaded) FilledButton.tonalIcon( onPressed: () => ref .read(offlineProvider.notifier) .deleteRegion(region.id), icon: const Icon(Icons.delete_outline), label: const Text('Delete'), ) else FilledButton.icon( onPressed: () => ref .read(offlineProvider.notifier) .downloadRegion(region), icon: const Icon(Icons.download), label: const Text('Download'), ), ], ), ], ), ), ); } } class _SizeChip extends StatelessWidget { final String label; final int sizeMb; const _SizeChip(this.label, this.sizeMb); @override Widget build(BuildContext context) { return Chip( label: Text('$label: $sizeMb MB'), materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, visualDensity: VisualDensity.compact, labelStyle: Theme.of(context).textTheme.labelSmall, ); } }