hondavert-dev/lib/ui/screens/graph/graph_screen.dart
HVBT Dev 763821171c feat: Add ECU Frame Debug Screen and DTC Screen
- Implemented RawFrameDebugScreen for displaying ECU frame data and diagnostics.
- Created DtcScreen to show active Diagnostic Trouble Codes (DTCs) with detailed descriptions.
- Added DtcRow widget for individual DTC representation.
- Introduced GraphScreen for visualizing sensor data over time with customizable time windows.
- Developed SensorListScreen to display available sensors and their current values.
- Enhanced SettingsScreen with options for Bluetooth connection, ECU protocol selection, polling interval, and theme settings.
- Added AppColors and AppTheme for consistent theming across the application.
- Implemented BtStatusChip to show Bluetooth connection status in the app bar.
- Created RecordFab for starting and stopping data recording sessions.
2026-07-26 23:14:58 +05:30

137 lines
4.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/models/sensor_defs.dart';
import '../../../core/providers/sensor_history_provider.dart';
import '../../../core/providers/theme_provider.dart';
import '../../../ui/theme/app_colors.dart';
import 'widgets/graph_time_selector.dart';
import 'widgets/sensor_graph_panel.dart';
final _graphWindowProvider = StateProvider<int>((ref) => 60);
final _graphSensorsProvider = StateProvider<List<String>>(
(ref) => ['rpm', 'map', 'tps'],
);
class GraphScreen extends ConsumerWidget {
const GraphScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final history = ref.watch(sensorHistoryProvider);
final colors = AppColors.forTheme(ref.watch(themeProvider));
final window = ref.watch(_graphWindowProvider);
final sensors = ref.watch(_graphSensorsProvider);
return Column(
children: [
// Time selector bar
Container(
color: const Color(0xFF1A1A1A),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
child: Row(
children: [
const Text('Window:',
style: TextStyle(color: Colors.grey, fontSize: 12)),
const SizedBox(width: 8),
GraphTimeSelector(
windowSeconds: window,
onChanged: (s) =>
ref.read(_graphWindowProvider.notifier).state = s,
),
const Spacer(),
IconButton(
icon: Icon(Icons.add_chart, color: colors.accent, size: 20),
tooltip: 'Add sensor',
onPressed: () => _addSensor(context, ref, sensors),
padding: EdgeInsets.zero,
constraints: const BoxConstraints(),
),
],
),
),
// Graph panels
Expanded(
child: sensors.isEmpty
? Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.show_chart,
size: 48, color: colors.accent.withAlpha(80)),
const SizedBox(height: 12),
const Text('No sensors selected',
style: TextStyle(color: Colors.grey)),
const SizedBox(height: 8),
ElevatedButton.icon(
onPressed: () => _addSensor(context, ref, sensors),
icon: const Icon(Icons.add, size: 16),
label: const Text('Add sensor'),
),
],
),
)
: ListView.builder(
itemCount: sensors.length,
itemBuilder: (context, i) => SensorGraphPanel(
sensorId: sensors[i],
history: history,
windowSeconds: window,
accentColor: colors.accent,
onRemove: () {
final next = List<String>.from(sensors)..removeAt(i);
ref.read(_graphSensorsProvider.notifier).state = next;
},
),
),
),
],
);
}
void _addSensor(
BuildContext context, WidgetRef ref, List<String> current) {
showModalBottomSheet<void>(
context: context,
backgroundColor: const Color(0xFF1A1A1A),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (_) => ListView(
children: [
const Padding(
padding: EdgeInsets.all(16),
child: Text('ADD SENSOR TO GRAPH',
style: TextStyle(
color: Colors.white70,
fontSize: 12,
letterSpacing: 2)),
),
const Divider(height: 1, color: Colors.white10),
...sensorDefs.map((def) {
final already = current.contains(def.id);
return ListTile(
title: Text(def.displayName,
style: TextStyle(
color: already ? Colors.grey : Colors.white)),
subtitle: Text(def.unit,
style: const TextStyle(
color: Colors.grey, fontSize: 11)),
trailing: already
? const Icon(Icons.check,
color: Colors.grey, size: 16)
: null,
onTap: already
? null
: () {
ref.read(_graphSensorsProvider.notifier).state =
[...current, def.id];
Navigator.pop(context);
},
);
}),
],
),
);
}
}