import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../services/location_service.dart'; import '../../services/sensor_fusion_service.dart'; import '../../services/compass_service.dart'; import '../rides/ride_state_machine.dart'; class DashboardScreen extends ConsumerWidget { const DashboardScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final locationAsync = ref.watch(locationStreamProvider); final leanAngleAsync = ref.watch(leanAngleStreamProvider); final gForceAsync = ref.watch(gForceStreamProvider); final headingAsync = ref.watch(headingStreamProvider); final rideSession = ref.watch(rideStateMachineProvider); // Speed calculation double currentSpeedMph = 0; if (locationAsync.hasValue && locationAsync.value != null) { currentSpeedMph = locationAsync.value!.speed * 2.23694; // We also update the state machine telemetry here for simplicity // In a real prod app, you might have a dedicated telemetry aggregator service WidgetsBinding.instance.addPostFrameCallback((_) { ref.read(rideStateMachineProvider.notifier).updateTelemetry( position: locationAsync.value, speedMph: currentSpeedMph, ); }); } double leanAngle = leanAngleAsync.value ?? 0.0; GForceData? gForce = gForceAsync.value; double heading = headingAsync.value ?? 0.0; return Scaffold( body: SafeArea( child: Padding( padding: const EdgeInsets.all(16.0), child: LayoutBuilder( builder: (context, constraints) { final isLandscape = constraints.maxWidth > constraints.maxHeight; if (isLandscape) { return _buildLandscapeLayout(context, currentSpeedMph, heading, leanAngle, gForce, rideSession); } return _buildPortraitLayout(context, currentSpeedMph, heading, leanAngle, gForce, rideSession); }, ), ), ), ); } Widget _buildPortraitLayout(BuildContext context, double speed, double heading, double lean, GForceData? gForce, RideSession session) { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ _buildCompassHeader(context, heading), const Spacer(), _buildSpeedometer(context, speed), const Spacer(), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildDataBox(context, 'LEAN', '${lean.abs().toStringAsFixed(0)}° ${lean > 0 ? 'R' : 'L'}'), _buildDataBox(context, 'G-FORCE', '${gForce?.longitudinal.abs().toStringAsFixed(2) ?? '0.00'} G'), _buildDataBox(context, 'TRIP', '${session.tripDistanceMiles.toStringAsFixed(2)} mi'), ], ), const SizedBox(height: 16), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildDataBox(context, '0-60', '-- s'), // Will connect to performance provider later _buildDataBox(context, 'TOP SPEED', '${session.topSpeedMph.toStringAsFixed(0)} MPH'), _buildDataBox(context, 'STATUS', session.state.name.toUpperCase()), ], ), const SizedBox(height: 32), _buildActionButtons(context, session), ], ); } Widget _buildLandscapeLayout(BuildContext context, double speed, double heading, double lean, GForceData? gForce, RideSession session) { return Row( children: [ Expanded( flex: 2, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildSpeedometer(context, speed), _buildActionButtons(context, session), ], ), ), Expanded( flex: 1, child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildCompassHeader(context, heading), _buildDataBox(context, 'LEAN', '${lean.abs().toStringAsFixed(0)}° ${lean > 0 ? 'R' : 'L'}'), _buildDataBox(context, 'G-FORCE', '${gForce?.longitudinal.abs().toStringAsFixed(2) ?? '0.00'} G'), _buildDataBox(context, 'TRIP', '${session.tripDistanceMiles.toStringAsFixed(2)} mi'), ], ), ), ], ); } Widget _buildCompassHeader(BuildContext context, double heading) { final compassService = CompassService(); // Just using for string conversion here final headingStr = compassService.getHeadingString(heading); return Column( children: [ Text(headingStr, style: Theme.of(context).textTheme.headlineLarge), Text('${heading.toStringAsFixed(0)}°', style: Theme.of(context).textTheme.labelLarge), ], ); } Widget _buildSpeedometer(BuildContext context, double speed) { return Column( children: [ Text( speed.toStringAsFixed(0), style: Theme.of(context).textTheme.displayLarge, textAlign: TextAlign.center, ), Text( 'MPH', style: Theme.of(context).textTheme.labelLarge, ), ], ); } Widget _buildDataBox(BuildContext context, String label, String value) { return Expanded( child: Column( children: [ Text(label, style: Theme.of(context).textTheme.labelLarge?.copyWith(fontSize: 16)), const SizedBox(height: 4), Text(value, style: Theme.of(context).textTheme.bodyLarge), ], ), ); } Widget _buildActionButtons(BuildContext context, RideSession session) { return Consumer( builder: (context, ref, child) { final notifier = ref.read(rideStateMachineProvider.notifier); if (session.state == RideState.idle || session.state == RideState.finished) { return ElevatedButton( onPressed: () => notifier.startRide(), child: const Padding( padding: EdgeInsets.all(16.0), child: Text('START RIDE', style: TextStyle(fontSize: 24)), ), ); } else if (session.state == RideState.riding) { return ElevatedButton( style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).colorScheme.secondary), onPressed: () => notifier.finishRide(), child: const Padding( padding: EdgeInsets.all(16.0), child: Text('END RIDE', style: TextStyle(fontSize: 24)), ), ); } return const SizedBox.shrink(); }, ); } }