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:
2026-05-20 19:08:05 +02:00
parent f185b3273a
commit 067012cc84
61 changed files with 7885 additions and 1 deletions
@@ -0,0 +1,82 @@
import 'package:flutter/material.dart';
import '../../../../extensions/date_time.dart';
/// Shows a scheduled time with optional realtime delay overlay.
///
/// Examples:
/// - on-time: `14:35`
/// - 2 minutes late: `14:35` + green/red `+2'` chip
/// - cancelled: scheduled time struck through, red `Ausfall` chip
class RealtimeTime extends StatelessWidget {
final DateTime scheduled;
final DateTime? realtime;
final int? delayMinutes;
final bool cancelled;
final TextStyle? style;
const RealtimeTime({
super.key,
required this.scheduled,
this.realtime,
this.delayMinutes,
this.cancelled = false,
this.style,
});
@override
Widget build(BuildContext context) {
final base = style ?? Theme.of(context).textTheme.bodyMedium ?? const TextStyle();
final scheduledText = Text(
scheduled.formatHm(),
style: base.copyWith(
decoration: cancelled ? TextDecoration.lineThrough : null,
),
);
if (cancelled) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
scheduledText,
const SizedBox(width: 6),
_badge(context, 'Ausfall', Colors.red),
],
);
}
final delay = delayMinutes;
if (delay != null && delay != 0) {
final positive = delay > 0;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
scheduledText,
const SizedBox(width: 4),
Text(
'${positive ? '+' : ''}$delay\'',
style: base.copyWith(
color: positive ? Colors.red : Colors.green,
fontWeight: FontWeight.bold,
),
),
],
);
}
return scheduledText;
}
Widget _badge(BuildContext context, String text, Color color) => Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 1),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(4),
),
child: Text(
text,
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
);
}