MotorSignal-App/lib/features/garage/garage_screen.dart

205 lines
8.1 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import '../../database/database.dart';
import '../../models/motorcycle_models.dart';
import 'garage_providers.dart';
class GarageScreen extends ConsumerWidget {
const GarageScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final bikesAsync = ref.watch(garageMotorcyclesStreamProvider);
return Scaffold(
appBar: AppBar(
title: const Text('My Garage', style: TextStyle(fontWeight: FontWeight.bold)),
actions: [
IconButton(
icon: const Icon(Icons.add_circle_outline, color: Colors.cyanAccent),
tooltip: 'Add Motorcycle',
onPressed: () => context.push('/garage/add'),
),
],
),
body: bikesAsync.when(
loading: () => const Center(child: CircularProgressIndicator()),
error: (err, stack) => Center(child: Text('Error loading garage: $err')),
data: (bikes) {
if (bikes.isEmpty) {
return _buildEmptyState(context);
}
return ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: bikes.length,
itemBuilder: (context, index) {
final bike = bikes[index];
return _buildMotorcycleCard(context, ref, bike);
},
);
},
),
floatingActionButton: bikesAsync.value != null && bikesAsync.value!.isNotEmpty
? FloatingActionButton.extended(
backgroundColor: Colors.cyanAccent,
foregroundColor: Colors.black,
icon: const Icon(Icons.two_wheeler),
label: const Text('ADD MOTORCYCLE', style: TextStyle(fontWeight: FontWeight.bold)),
onPressed: () => context.push('/garage/add'),
)
: null,
);
}
Widget _buildEmptyState(BuildContext context) {
return Center(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.two_wheeler, size: 80, color: Colors.white24),
const SizedBox(height: 24),
const Text(
'YOUR GARAGE IS EMPTY',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold, letterSpacing: 1.5),
),
const SizedBox(height: 12),
const Text(
'Add your motorcycle to personalize your ride history and performance data.',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white70, height: 1.4),
),
const SizedBox(height: 32),
ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.cyanAccent,
foregroundColor: Colors.black,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 14),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
),
icon: const Icon(Icons.add),
label: const Text('ADD MOTORCYCLE', style: TextStyle(fontWeight: FontWeight.bold, letterSpacing: 1.2)),
onPressed: () => context.push('/garage/add'),
),
],
),
),
);
}
Widget _buildMotorcycleCard(BuildContext context, WidgetRef ref, GarageMotorcycle bike) {
MotorcycleSpecification? spec;
if (bike.specificationJson != null) {
try {
spec = MotorcycleSpecification.fromJson(json.decode(bike.specificationJson!));
} catch (_) {}
}
final displayName = bike.nickname != null && bike.nickname!.isNotEmpty
? '${bike.nickname} (${bike.year} ${bike.makeName} ${bike.modelName})'
: '${bike.year} ${bike.makeName} ${bike.modelName}';
return Card(
margin: const EdgeInsets.only(bottom: 16),
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(
color: bike.isDefault ? Colors.cyanAccent : Colors.white12,
width: bike.isDefault ? 2 : 1,
),
),
child: InkWell(
onTap: () => context.push('/garage/${bike.id}'),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white10,
borderRadius: BorderRadius.circular(10),
),
child: const Icon(Icons.two_wheeler, size: 36, color: Colors.cyanAccent),
),
const SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: Text(
displayName,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
if (bike.isDefault)
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
decoration: BoxDecoration(
color: Colors.cyanAccent.withValues(alpha: 0.2),
borderRadius: BorderRadius.circular(4),
border: Border.all(color: Colors.cyanAccent),
),
child: const Text(
'CURRENT',
style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold, color: Colors.cyanAccent),
),
),
],
),
const SizedBox(height: 6),
Text(
spec != null
? '${spec.displacementCc?.toStringAsFixed(0) ?? "—"} cc • ${spec.horsepower?.toStringAsFixed(0) ?? "—"} hp'
: '${bike.year} • ${bike.makeName}',
style: const TextStyle(color: Colors.white70, fontSize: 14),
),
],
),
),
],
),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
TextButton.icon(
icon: const Icon(Icons.directions_bike, size: 18),
label: const Text('View Specs & Stats'),
onPressed: () => context.push('/garage/${bike.id}'),
),
if (!bike.isDefault)
OutlinedButton(
style: OutlinedButton.styleFrom(
foregroundColor: Colors.white,
side: const BorderSide(color: Colors.white30),
),
onPressed: () {
ref.read(garageControllerProvider).setDefaultMotorcycle(bike.id);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Set ${bike.makeName} ${bike.modelName} as current motorcycle.')),
);
},
child: const Text('Set as Current'),
),
],
),
],
),
),
),
);
}
}