first app design

This commit is contained in:
root
2026-05-24 02:35:37 -04:00
parent c842eed601
commit d04c8ce437
138 changed files with 9672 additions and 0 deletions
@@ -0,0 +1,203 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../providers/dashboard_providers.dart';
import '../../../core/constants/app_constants.dart';
import '../../../widgets/status_badge.dart';
import '../../../widgets/error_view.dart';
class VehicleDetailScreen extends ConsumerWidget {
final String id;
const VehicleDetailScreen({super.key, required this.id});
@override
Widget build(BuildContext context, WidgetRef ref) {
final vehicleAsync = ref.watch(vehicleDetailProvider(id));
return vehicleAsync.when(
loading: () => const Scaffold(
body: Center(child: CircularProgressIndicator()),
),
error: (e, _) => Scaffold(
appBar: AppBar(),
body: ErrorView(
message: 'Could not load vehicle',
onRetry: () => ref.invalidate(vehicleDetailProvider(id)),
),
),
data: (vehicle) {
final baseUrl =
AppConstants.baseUrl.replaceAll('/api/v1', '');
return Scaffold(
body: CustomScrollView(
slivers: [
SliverAppBar(
expandedHeight: 240,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
background: vehicle.primaryPhoto != null
? CachedNetworkImage(
imageUrl: vehicle.primaryPhoto!.startsWith('http')
? vehicle.primaryPhoto!
: '$baseUrl${vehicle.primaryPhoto!}',
fit: BoxFit.cover,
errorWidget: (_, _, _) => Container(
color: const Color(0xFFF3F4F6),
child: const Icon(Icons.directions_car,
size: 72, color: Color(0xFFD1D5DB)),
),
)
: Container(
color: const Color(0xFFF3F4F6),
child: const Icon(Icons.directions_car,
size: 72, color: Color(0xFFD1D5DB)),
),
),
),
SliverPadding(
padding: const EdgeInsets.all(20),
sliver: SliverList(
delegate: SliverChildListDelegate([
Row(
children: [
Expanded(
child: Text(
vehicle.displayName,
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
color: Color(0xFF111928),
),
),
),
StatusBadge(status: vehicle.status),
],
),
const SizedBox(height: 8),
Text(
vehicle.licensePlate,
style: const TextStyle(
fontSize: 15, color: Color(0xFF6B7280)),
),
const SizedBox(height: 20),
_InfoGrid(vehicle: vehicle),
const SizedBox(height: 20),
const Text(
'Pricing',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Color(0xFF111928),
),
),
const SizedBox(height: 12),
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const Icon(Icons.attach_money,
color: Color(0xFF1A56DB)),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'\$${vehicle.dailyRate.toStringAsFixed(2)}/day',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Color(0xFF1A56DB),
),
),
const Text(
'Daily rate',
style: TextStyle(
fontSize: 12,
color: Color(0xFF6B7280)),
),
],
),
const Spacer(),
Switch(
value: vehicle.isPublished,
onChanged: (v) async {
try {
await ref
.read(vehicleServiceProvider)
.togglePublish(id, v);
ref.invalidate(vehicleDetailProvider(id));
} catch (_) {}
},
),
const Text(
'Published',
style: TextStyle(
fontSize: 12, color: Color(0xFF6B7280)),
),
],
),
),
),
]),
),
),
],
),
);
},
);
}
}
class _InfoGrid extends StatelessWidget {
final dynamic vehicle;
const _InfoGrid({required this.vehicle});
@override
Widget build(BuildContext context) {
final items = [
('Category', vehicle.category),
if (vehicle.seats != null) ('Seats', '${vehicle.seats}'),
if (vehicle.transmission != null) ('Transmission', vehicle.transmission!),
if (vehicle.fuelType != null) ('Fuel', vehicle.fuelType!),
if (vehicle.mileage != null)
('Mileage', '${vehicle.mileage} km'),
];
return Wrap(
spacing: 12,
runSpacing: 12,
children: items
.map((item) => Container(
padding: const EdgeInsets.symmetric(
horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: const Color(0xFFF3F4F6),
borderRadius: BorderRadius.circular(10),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item.$1,
style: const TextStyle(
fontSize: 11, color: Color(0xFF9CA3AF)),
),
const SizedBox(height: 2),
Text(
item.$2,
style: const TextStyle(
fontWeight: FontWeight.w600,
color: Color(0xFF111928),
),
),
],
),
))
.toList(),
);
}
}