implemented RMV public transit module including trip search, station departures, and nearby stop lookup, added "Marianum Connect" API integration with bearer token authentication and auto-refresh logic, integrated geolocator for location-based station search, added persistent storage for favorite stations and recent trip queries, and implemented comprehensive UI for journey details, trip results, and disruption alerts
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../../api/connect/rmv/rmv_models.dart';
|
||||
import 'product_chip.dart';
|
||||
import 'realtime_time.dart';
|
||||
|
||||
/// Renders a single departure or arrival row. Used in the station detail view.
|
||||
class DepartureArrivalTile extends StatelessWidget {
|
||||
final Product? product;
|
||||
final String name;
|
||||
|
||||
/// Direction (for departures) or origin (for arrivals).
|
||||
final String towards;
|
||||
final DateTime scheduled;
|
||||
final DateTime? realtime;
|
||||
final int? delayMinutes;
|
||||
final String? track;
|
||||
final String? realTrack;
|
||||
final bool cancelled;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
const DepartureArrivalTile({
|
||||
super.key,
|
||||
required this.product,
|
||||
required this.name,
|
||||
required this.towards,
|
||||
required this.scheduled,
|
||||
this.realtime,
|
||||
this.delayMinutes,
|
||||
this.track,
|
||||
this.realTrack,
|
||||
this.cancelled = false,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
factory DepartureArrivalTile.fromDeparture(
|
||||
Departure d, {
|
||||
VoidCallback? onTap,
|
||||
}) => DepartureArrivalTile(
|
||||
product: d.product,
|
||||
name: d.name,
|
||||
towards: 'nach ${d.direction}',
|
||||
scheduled: d.scheduledTime,
|
||||
realtime: d.realTime,
|
||||
delayMinutes: d.delayMinutes,
|
||||
track: d.track,
|
||||
realTrack: d.realTrack,
|
||||
cancelled: d.cancelled,
|
||||
onTap: onTap,
|
||||
);
|
||||
|
||||
factory DepartureArrivalTile.fromArrival(
|
||||
Arrival a, {
|
||||
VoidCallback? onTap,
|
||||
}) => DepartureArrivalTile(
|
||||
product: a.product,
|
||||
name: a.name,
|
||||
towards: 'von ${a.origin}',
|
||||
scheduled: a.scheduledTime,
|
||||
realtime: a.realTime,
|
||||
delayMinutes: a.delayMinutes,
|
||||
track: a.track,
|
||||
realTrack: a.realTrack,
|
||||
cancelled: a.cancelled,
|
||||
onTap: onTap,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final effectiveTrack = (realTrack?.isNotEmpty ?? false)
|
||||
? realTrack!
|
||||
: (track ?? '');
|
||||
final trackChanged =
|
||||
realTrack != null && track != null && realTrack != track;
|
||||
return ListTile(
|
||||
onTap: onTap,
|
||||
leading: SizedBox(
|
||||
width: 72,
|
||||
child: ProductChip(product: product, fallbackLabel: name),
|
||||
),
|
||||
title: Text(
|
||||
towards,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
decoration: cancelled ? TextDecoration.lineThrough : null,
|
||||
),
|
||||
),
|
||||
subtitle: effectiveTrack.isEmpty
|
||||
? null
|
||||
: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.directions_transit,
|
||||
size: 14,
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
'Gleis $effectiveTrack',
|
||||
style: TextStyle(
|
||||
color: trackChanged
|
||||
? Colors.red
|
||||
: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: trackChanged ? FontWeight.bold : null,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: RealtimeTime(
|
||||
scheduled: scheduled,
|
||||
realtime: realtime,
|
||||
delayMinutes: delayMinutes,
|
||||
cancelled: cancelled,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user