import 'package:flutter/material.dart'; import '../../../../core/models/sensor_defs.dart'; import '../../../../core/protocol/sensor_state.dart'; class SensorTile extends StatelessWidget { final int slotIndex; final String sensorId; final SensorState state; final Color accentColor; final VoidCallback onTap; const SensorTile({ super.key, required this.slotIndex, required this.sensorId, required this.state, required this.accentColor, required this.onTap, }); @override Widget build(BuildContext context) { final def = sensorDefs.firstWhere( (d) => d.id == sensorId, orElse: () => sensorDefs.first, ); final value = _getValue(state, sensorId); final normalized = ((value - def.min) / (def.max - def.min)).clamp(0.0, 1.0); return GestureDetector( onLongPress: onTap, child: Container( decoration: BoxDecoration( color: const Color(0xFF1A1A1A), borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.white10), ), padding: const EdgeInsets.fromLTRB(10, 8, 10, 6), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( def.displayName.toUpperCase(), style: const TextStyle( color: Colors.grey, fontSize: 10, letterSpacing: 1, ), ), Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( _format(value, sensorId), style: const TextStyle( color: Colors.white, fontSize: 22, fontWeight: FontWeight.bold, letterSpacing: -0.5, ), ), const SizedBox(width: 3), Padding( padding: const EdgeInsets.only(bottom: 2), child: Text( def.unit, style: TextStyle(color: accentColor, fontSize: 11), ), ), ], ), // Bar gauge ClipRRect( borderRadius: BorderRadius.circular(2), child: LinearProgressIndicator( value: normalized, minHeight: 4, backgroundColor: Colors.white10, valueColor: AlwaysStoppedAnimation( normalized > 0.8 ? Colors.red : accentColor, ), ), ), ], ), ), ); } String _format(double v, String id) { if (id == 'gear') return v.toStringAsFixed(0); if (id == 'rpm') return v.toStringAsFixed(0); if (v.abs() >= 100) return v.toStringAsFixed(1); return v.toStringAsFixed(2); } } double _getValue(SensorState s, String id) { switch (id) { case 'rpm': return s.rpm; case 'vss': return s.vss; case 'map': return s.map; case 'tps': return s.tps; case 'inj': return s.inj; case 'ign': return s.ign; case 'ect': return s.ect; case 'iat': return s.iat; case 'bat': return s.bat; case 'o2': return s.o2; case 'gear': return s.gear.toDouble(); case 'eth': return s.eth; case 'pa': return s.pa; case 'afr': return s.afr; case 'strim': return s.strim; case 'ltrim': return s.ltrim; case 'ain0': return s.ain0; case 'ain1': return s.ain1; case 'ain2': return s.ain2; case 'ain3': return s.ain3; case 'ain4': return s.ain4; case 'ain5': return s.ain5; case 'ain6': return s.ain6; case 'ain7': return s.ain7; default: return 0; } }