import 'dart:async'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../core/theme/app_colors.dart'; import '../../core/widgets/app_icon.dart'; import '../../data/models/bluetooth_device_info.dart'; import '../home/obd_controller.dart'; import '../home/weather_controller.dart'; /// Settings card for the optional ELM327 adapter and location weather. class CarDataSection extends StatefulWidget { const CarDataSection({super.key}); @override State createState() => _CarDataSectionState(); } class _CarDataSectionState extends State { @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; unawaited( context.read().refreshDevices(requestPermission: false), ); }); } @override Widget build(BuildContext context) { final colors = context.colors; final theme = Theme.of(context); final obd = context.watch(); final weather = context.watch(); final selected = obd.selectedAddress; final options = [...obd.devices]; if (selected != null && !options.any((d) => d.address == selected)) { options.add( BluetoothDeviceInfo(name: 'Saved adapter', address: selected), ); } final selectedInOptions = options.any((d) => d.address == selected); final dropdownValue = selectedInOptions ? selected : null; final statusColor = obd.connected ? colors.accent : obd.error != null ? colors.danger : colors.inkFaint; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 42, height: 42, decoration: BoxDecoration( color: colors.accentSoft, borderRadius: BorderRadius.circular(14), ), alignment: Alignment.center, child: AppIcon( 'assets/icons/car.svg', size: 20, color: colors.accent, ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('OBD-II adapter', style: theme.textTheme.titleMedium), const SizedBox(height: 2), Row( children: [ Container( width: 7, height: 7, decoration: BoxDecoration( color: statusColor, shape: BoxShape.circle, ), ), const SizedBox(width: 7), Expanded( child: Text( obd.statusLabel, maxLines: 1, overflow: TextOverflow.ellipsis, style: theme.textTheme.bodySmall?.copyWith( color: colors.inkMuted, ), ), ), ], ), ], ), ), TextButton.icon( onPressed: obd.loadingDevices ? null : () async { final ok = await obd.refreshDevices( requestPermission: true, ); if (!ok && context.mounted) { _message( context, obd.devicesError ?? 'Could not read paired Bluetooth devices.', ); } }, icon: AppIcon( 'assets/icons/search.svg', size: 16, color: colors.accent, ), label: Text(obd.needsBluetoothPermission ? 'Allow' : 'Find'), style: TextButton.styleFrom(foregroundColor: colors.accent), ), ], ), const SizedBox(height: 12), DropdownButtonFormField( key: ValueKey(dropdownValue), initialValue: dropdownValue, isExpanded: true, dropdownColor: colors.surface, style: theme.textTheme.bodyLarge?.copyWith(color: colors.ink), decoration: const InputDecoration( labelText: 'Bluetooth device', isDense: true, ), items: [ const DropdownMenuItem(value: null, child: Text('None')), for (final device in options) DropdownMenuItem( value: device.address, child: Text( device.label, maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ], onChanged: (value) async { await obd.selectAdapter(value); if (!context.mounted) return; if (obd.error != null) { _message(context, obd.error!); } }, ), if (obd.devicesError != null && !obd.needsBluetoothPermission) ...[ const SizedBox(height: 6), Text( obd.devicesError!, style: theme.textTheme.bodySmall?.copyWith(color: colors.danger), ), ], const SizedBox(height: 8), Text( 'Pair the ELM327 in Android Bluetooth settings first. CarDash only ' 'reads standard OBD-II PIDs; it never writes.', style: theme.textTheme.bodySmall?.copyWith(color: colors.inkMuted), ), Divider(color: colors.border, height: 30), Row( children: [ Container( width: 38, height: 38, decoration: BoxDecoration( color: colors.surfaceStrong, borderRadius: BorderRadius.circular(13), border: Border.all(color: colors.border), ), alignment: Alignment.center, child: AppIcon( 'assets/icons/cloud.svg', size: 18, color: colors.inkMuted, ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Location temperature', style: theme.textTheme.bodyLarge, ), const SizedBox(height: 2), Text( 'Show temperature from current location if no OBD-II connected.', style: theme.textTheme.bodySmall?.copyWith( color: colors.inkMuted, ), ), ], ), ), const SizedBox(width: 8), Switch( value: weather.enabled, onChanged: (value) async { final ok = await weather.setEnabled(value); if (!ok && context.mounted) { _message( context, weather.error ?? 'Location permission is required for weather.', ); } }, thumbColor: WidgetStateProperty.resolveWith( (states) => states.contains(WidgetState.selected) ? colors.accentInk : colors.inkFaint, ), trackColor: WidgetStateProperty.resolveWith( (states) => states.contains(WidgetState.selected) ? colors.accent : colors.surfaceStrong, ), trackOutlineColor: WidgetStatePropertyAll(colors.border), ), ], ), if (weather.enabled && weather.error != null) ...[ const SizedBox(height: 4), Text( weather.error!, style: theme.textTheme.bodySmall?.copyWith(color: colors.inkMuted), ), ], ], ); } void _message(BuildContext context, String message) { final messenger = ScaffoldMessenger.of(context); messenger.hideCurrentSnackBar(); messenger.showSnackBar(SnackBar(content: Text(message))); } }