hondavert-dev/lib/ui/screens/datalog/datalog_list_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

125 lines
4.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import '../../../core/models/datalog_session.dart';
import '../../../core/providers/datalog_provider.dart';
import 'datalog_playback_bar.dart';
class DatalogListScreen extends ConsumerWidget {
const DatalogListScreen({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final sessionsAsync = ref.watch(sessionListProvider);
final playback = ref.watch(playbackProvider);
return Scaffold(
appBar: AppBar(title: const Text('Saved Sessions')),
body: Column(
children: [
if (playback.isActive)
DatalogPlaybackBar(state: playback),
Expanded(
child: sessionsAsync.when(
loading: () =>
const Center(child: CircularProgressIndicator()),
error: (e, _) => Center(
child: Text('Error: $e',
style: const TextStyle(color: Colors.red))),
data: (sessions) => sessions.isEmpty
? const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.videocam_off,
size: 56, color: Colors.grey),
SizedBox(height: 12),
Text('No recorded sessions',
style: TextStyle(color: Colors.grey)),
],
),
)
: ListView.separated(
itemCount: sessions.length,
separatorBuilder: (_, __) =>
const Divider(height: 1, color: Colors.white10),
itemBuilder: (context, i) =>
_SessionTile(session: sessions[i]),
),
),
),
],
),
);
}
}
class _SessionTile extends ConsumerWidget {
final DatalogSession session;
const _SessionTile({required this.session});
@override
Widget build(BuildContext context, WidgetRef ref) {
final fmt = DateFormat('dd MMM yyyy HH:mm');
final isPlaying =
ref.watch(playbackProvider).sessionId == session.id;
return Dismissible(
key: ValueKey(session.id),
direction: DismissDirection.endToStart,
background: Container(
color: Colors.red,
alignment: Alignment.centerRight,
padding: const EdgeInsets.only(right: 20),
child: const Icon(Icons.delete, color: Colors.white),
),
confirmDismiss: (_) async {
return await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('Delete session?'),
content: const Text('This cannot be undone.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: const Text('Cancel')),
TextButton(
onPressed: () => Navigator.pop(ctx, true),
child: const Text('Delete',
style: TextStyle(color: Colors.red))),
],
),
);
},
onDismissed: (_) async {
await ref
.read(datalogDbProvider)
.deleteSession(session.id!);
ref.invalidate(sessionListProvider);
},
child: ListTile(
leading: Icon(
isPlaying ? Icons.play_circle : Icons.videocam,
color: isPlaying ? Colors.green : Colors.grey,
),
title: Text(
fmt.format(session.startTime),
style: const TextStyle(fontSize: 13),
),
subtitle: Text(
'${session.ecuType.toUpperCase()}'
'${session.frameCount} frames • ${session.durationLabel}',
style: const TextStyle(color: Colors.grey, fontSize: 11),
),
trailing: const Icon(Icons.play_arrow, size: 18),
onTap: () async {
await ref
.read(playbackProvider.notifier)
.loadSession(session.id!);
ref.read(playbackProvider.notifier).play();
},
),
);
}
}