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
+36
View File
@@ -0,0 +1,36 @@
/// ISO-8601 duration (`PT1H30M5S`) ↔ Dart `Duration`. Backend serialises
/// `java.time.Duration` in this format; Dart has no builtin parser.
class IsoDuration {
IsoDuration._();
static final RegExp _pattern = RegExp(
r'^P(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+(?:\.\d+)?)S)?)?$',
);
static Duration? fromJson(String? iso) {
if (iso == null || iso.isEmpty) return null;
final match = _pattern.firstMatch(iso);
if (match == null) return null;
final hours = int.parse(match.group(1) ?? '0');
final minutes = int.parse(match.group(2) ?? '0');
final secondsRaw = match.group(3) ?? '0';
final secondsValue = double.parse(secondsRaw);
return Duration(
hours: hours,
minutes: minutes,
milliseconds: (secondsValue * 1000).round(),
);
}
static String? toJson(Duration? d) {
if (d == null) return null;
final hours = d.inHours;
final minutes = d.inMinutes.remainder(60);
final seconds = d.inSeconds.remainder(60);
final buf = StringBuffer('PT');
if (hours > 0) buf.write('${hours}H');
if (minutes > 0) buf.write('${minutes}M');
if (seconds > 0 || (hours == 0 && minutes == 0)) buf.write('${seconds}S');
return buf.toString();
}
}
@@ -0,0 +1,13 @@
/// Formats a [DateTime] as `2026-05-19T14:30:00` for Java's
/// `LocalDateTime` parser (no timezone, no millis).
String formatLocalDateTime(DateTime dt) {
String two(int v) => v.toString().padLeft(2, '0');
return '${dt.year}-${two(dt.month)}-${two(dt.day)}T'
'${two(dt.hour)}:${two(dt.minute)}:${two(dt.second)}';
}
/// Formats a [DateTime] as `2026-05-19` for Java's `LocalDate` parser.
String formatLocalDate(DateTime dt) {
String two(int v) => v.toString().padLeft(2, '0');
return '${dt.year}-${two(dt.month)}-${two(dt.day)}';
}
@@ -0,0 +1,32 @@
import 'dart:convert';
import '../../connect_api.dart';
import '../rmv_models.dart';
import '_query_format.dart';
class GetArrivals extends ConnectApi<List<Arrival>> {
final String stopId;
final DateTime? when;
final int durationMinutes;
final int maxJourneys;
GetArrivals({
required this.stopId,
this.when,
this.durationMinutes = 60,
this.maxJourneys = -1,
}) : super('rmv/arrivals');
@override
Map<String, String>? get queryParameters => {
'stopId': stopId,
if (when != null) 'when': formatLocalDateTime(when!),
'duration': durationMinutes.toString(),
'max': maxJourneys.toString(),
};
@override
List<Arrival> assemble(String raw) => (jsonDecode(raw) as List)
.map((e) => Arrival.fromJson(e as Map<String, dynamic>))
.toList(growable: false);
}
@@ -0,0 +1,32 @@
import 'dart:convert';
import '../../connect_api.dart';
import '../rmv_models.dart';
import '_query_format.dart';
class GetDepartures extends ConnectApi<List<Departure>> {
final String stopId;
final DateTime? when;
final int durationMinutes;
final int maxJourneys;
GetDepartures({
required this.stopId,
this.when,
this.durationMinutes = 60,
this.maxJourneys = -1,
}) : super('rmv/departures');
@override
Map<String, String>? get queryParameters => {
'stopId': stopId,
if (when != null) 'when': formatLocalDateTime(when!),
'duration': durationMinutes.toString(),
'max': maxJourneys.toString(),
};
@override
List<Departure> assemble(String raw) => (jsonDecode(raw) as List)
.map((e) => Departure.fromJson(e as Map<String, dynamic>))
.toList(growable: false);
}
@@ -0,0 +1,20 @@
import 'dart:convert';
import '../../connect_api.dart';
import '../rmv_models.dart';
import '_query_format.dart';
class GetDisruptions extends ConnectApi<List<HimMessage>> {
final DateTime? when;
GetDisruptions({this.when}) : super('rmv/disruptions');
@override
Map<String, String>? get queryParameters =>
when == null ? null : {'when': formatLocalDateTime(when!)};
@override
List<HimMessage> assemble(String raw) => (jsonDecode(raw) as List)
.map((e) => HimMessage.fromJson(e as Map<String, dynamic>))
.toList(growable: false);
}
@@ -0,0 +1,23 @@
import 'dart:convert';
import '../../connect_api.dart';
import '../rmv_models.dart';
import '_query_format.dart';
class GetJourneyDetail extends ConnectApi<JourneyDetail> {
final String journeyRef;
final DateTime? date;
GetJourneyDetail({required this.journeyRef, this.date})
: super('rmv/journey');
@override
Map<String, String>? get queryParameters => {
'ref': journeyRef,
if (date != null) 'date': formatLocalDate(date!),
};
@override
JourneyDetail assemble(String raw) =>
JourneyDetail.fromJson(jsonDecode(raw) as Map<String, dynamic>);
}
@@ -0,0 +1,17 @@
import 'dart:convert';
import '../../connect_api.dart';
import '../rmv_models.dart';
class MoreTrips extends ConnectApi<TripSearchResult> {
final String ctx;
MoreTrips({required this.ctx}) : super('rmv/trips/more');
@override
Map<String, String>? get queryParameters => {'ctx': ctx};
@override
TripSearchResult assemble(String raw) =>
TripSearchResult.fromJson(jsonDecode(raw) as Map<String, dynamic>);
}
@@ -0,0 +1,31 @@
import 'dart:convert';
import '../../connect_api.dart';
import '../rmv_models.dart';
class NearbyStops extends ConnectApi<List<StopLocation>> {
final double lat;
final double lon;
final int radiusMeters;
final int max;
NearbyStops({
required this.lat,
required this.lon,
this.radiusMeters = 1000,
this.max = 20,
}) : super('rmv/stops/nearby');
@override
Map<String, String>? get queryParameters => {
'lat': lat.toString(),
'lon': lon.toString(),
'radius': radiusMeters.toString(),
'max': max.toString(),
};
@override
List<StopLocation> assemble(String raw) => (jsonDecode(raw) as List)
.map((e) => StopLocation.fromJson(e as Map<String, dynamic>))
.toList(growable: false);
}
@@ -0,0 +1,22 @@
import 'dart:convert';
import '../../connect_api.dart';
import '../rmv_models.dart';
class SearchStops extends ConnectApi<List<StopLocation>> {
final String query;
final int max;
SearchStops({required this.query, this.max = 10}) : super('rmv/stops');
@override
Map<String, String>? get queryParameters => {
'q': query,
'max': max.toString(),
};
@override
List<StopLocation> assemble(String raw) => (jsonDecode(raw) as List)
.map((e) => StopLocation.fromJson(e as Map<String, dynamic>))
.toList(growable: false);
}
@@ -0,0 +1,31 @@
import 'dart:convert';
import '../../connect_api.dart';
import '../rmv_models.dart';
import '_query_format.dart';
class SearchTrips extends ConnectApi<TripSearchResult> {
final String fromStopId;
final String toStopId;
final DateTime? when;
final bool searchByArrival;
SearchTrips({
required this.fromStopId,
required this.toStopId,
this.when,
this.searchByArrival = false,
}) : super('rmv/trips');
@override
Map<String, String>? get queryParameters => {
'from': fromStopId,
'to': toStopId,
if (when != null) 'when': formatLocalDateTime(when!),
'searchByArrival': searchByArrival.toString(),
};
@override
TripSearchResult assemble(String raw) =>
TripSearchResult.fromJson(jsonDecode(raw) as Map<String, dynamic>);
}
+243
View File
@@ -0,0 +1,243 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import 'iso_duration.dart';
part 'rmv_models.freezed.dart';
part 'rmv_models.g.dart';
@freezed
abstract class Product with _$Product {
const factory Product({
String? name,
String? line,
String? displayNumber,
String? category,
String? categoryCode,
String? operator,
}) = _Product;
factory Product.fromJson(Map<String, Object?> json) =>
_$ProductFromJson(json);
}
@freezed
abstract class StopLocation with _$StopLocation {
const factory StopLocation({
required String id,
String? extId,
required String name,
String? description,
double? lat,
double? lon,
int? products,
int? distanceMeters,
}) = _StopLocation;
factory StopLocation.fromJson(Map<String, Object?> json) =>
_$StopLocationFromJson(json);
}
@freezed
abstract class Departure with _$Departure {
const factory Departure({
required String stopId,
String? stopExtId,
required String stopName,
required String name,
required String direction,
String? directionFlag,
required DateTime scheduledTime,
DateTime? realTime,
int? delayMinutes,
String? track,
String? realTrack,
@Default(false) bool cancelled,
@Default(true) bool reachable,
Product? product,
String? journeyRef,
}) = _Departure;
factory Departure.fromJson(Map<String, Object?> json) =>
_$DepartureFromJson(json);
}
@freezed
abstract class Arrival with _$Arrival {
const factory Arrival({
required String stopId,
String? stopExtId,
required String stopName,
required String name,
required String origin,
required DateTime scheduledTime,
DateTime? realTime,
int? delayMinutes,
String? track,
String? realTrack,
@Default(false) bool cancelled,
Product? product,
String? journeyRef,
}) = _Arrival;
factory Arrival.fromJson(Map<String, Object?> json) =>
_$ArrivalFromJson(json);
}
@freezed
abstract class TripEndpoint with _$TripEndpoint {
const factory TripEndpoint({
required String stopId,
String? stopExtId,
required String name,
double? lat,
double? lon,
required DateTime scheduledTime,
DateTime? realTime,
int? delayMinutes,
String? track,
String? realTrack,
String? type,
}) = _TripEndpoint;
factory TripEndpoint.fromJson(Map<String, Object?> json) =>
_$TripEndpointFromJson(json);
}
@freezed
abstract class JourneyStop with _$JourneyStop {
const factory JourneyStop({
required String id,
String? extId,
required String name,
double? lat,
double? lon,
int? routeIdx,
DateTime? scheduledArrival,
DateTime? scheduledDeparture,
DateTime? realArrival,
DateTime? realDeparture,
String? arrTrack,
String? depTrack,
String? realArrTrack,
String? realDepTrack,
@Default(false) bool cancelled,
@Default(false) bool cancelledArrival,
@Default(false) bool cancelledDeparture,
}) = _JourneyStop;
factory JourneyStop.fromJson(Map<String, Object?> json) =>
_$JourneyStopFromJson(json);
}
enum LegType {
@JsonValue('JOURNEY')
journey,
@JsonValue('WALK')
walk,
@JsonValue('TRANSFER')
transfer,
@JsonValue('BIKE')
bike,
@JsonValue('CAR')
car,
@JsonValue('PARK_RIDE')
parkRide,
@JsonValue('TAXI')
taxi,
@JsonValue('CHECK_IN')
checkIn,
@JsonValue('CHECK_OUT')
checkOut,
@JsonValue('DUMMY')
dummy,
@JsonValue('UNKNOWN')
unknown,
}
@freezed
abstract class Leg with _$Leg {
const factory Leg({
required String id,
required int idx,
@Default(LegType.unknown) LegType type,
String? name,
String? category,
String? number,
String? direction,
required TripEndpoint origin,
required TripEndpoint destination,
@JsonKey(fromJson: IsoDuration.fromJson, toJson: IsoDuration.toJson)
Duration? duration,
@Default(false) bool cancelled,
@Default(false) bool partCancelled,
@Default(true) bool reachable,
Product? product,
String? journeyRef,
@Default(<JourneyStop>[]) List<JourneyStop> stops,
}) = _Leg;
factory Leg.fromJson(Map<String, Object?> json) => _$LegFromJson(json);
}
@freezed
abstract class Trip with _$Trip {
const factory Trip({
String? tripId,
String? ctxRecon,
String? checksum,
@JsonKey(fromJson: IsoDuration.fromJson, toJson: IsoDuration.toJson)
Duration? duration,
@JsonKey(fromJson: IsoDuration.fromJson, toJson: IsoDuration.toJson)
Duration? realDuration,
int? transferCount,
@Default(<Leg>[]) List<Leg> legs,
}) = _Trip;
factory Trip.fromJson(Map<String, Object?> json) => _$TripFromJson(json);
}
@freezed
abstract class TripSearchResult with _$TripSearchResult {
const factory TripSearchResult({
@Default(<Trip>[]) List<Trip> trips,
String? scrollContextLater,
String? scrollContextEarlier,
}) = _TripSearchResult;
factory TripSearchResult.fromJson(Map<String, Object?> json) =>
_$TripSearchResultFromJson(json);
}
@freezed
abstract class JourneyDetail with _$JourneyDetail {
const factory JourneyDetail({
String? journeyId,
Product? product,
String? direction,
@Default(<JourneyStop>[]) List<JourneyStop> stops,
}) = _JourneyDetail;
factory JourneyDetail.fromJson(Map<String, Object?> json) =>
_$JourneyDetailFromJson(json);
}
@freezed
abstract class HimMessage with _$HimMessage {
const factory HimMessage({
required String id,
String? externalId,
String? head,
String? lead,
String? text,
String? category,
String? company,
int? priority,
int? products,
DateTime? startValidity,
DateTime? endValidity,
DateTime? modified,
}) = _HimMessage;
factory HimMessage.fromJson(Map<String, Object?> json) =>
_$HimMessageFromJson(json);
}
File diff suppressed because it is too large Load Diff
+368
View File
@@ -0,0 +1,368 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'rmv_models.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_Product _$ProductFromJson(Map<String, dynamic> json) => _Product(
name: json['name'] as String?,
line: json['line'] as String?,
displayNumber: json['displayNumber'] as String?,
category: json['category'] as String?,
categoryCode: json['categoryCode'] as String?,
operator: json['operator'] as String?,
);
Map<String, dynamic> _$ProductToJson(_Product instance) => <String, dynamic>{
'name': instance.name,
'line': instance.line,
'displayNumber': instance.displayNumber,
'category': instance.category,
'categoryCode': instance.categoryCode,
'operator': instance.operator,
};
_StopLocation _$StopLocationFromJson(Map<String, dynamic> json) =>
_StopLocation(
id: json['id'] as String,
extId: json['extId'] as String?,
name: json['name'] as String,
description: json['description'] as String?,
lat: (json['lat'] as num?)?.toDouble(),
lon: (json['lon'] as num?)?.toDouble(),
products: (json['products'] as num?)?.toInt(),
distanceMeters: (json['distanceMeters'] as num?)?.toInt(),
);
Map<String, dynamic> _$StopLocationToJson(_StopLocation instance) =>
<String, dynamic>{
'id': instance.id,
'extId': instance.extId,
'name': instance.name,
'description': instance.description,
'lat': instance.lat,
'lon': instance.lon,
'products': instance.products,
'distanceMeters': instance.distanceMeters,
};
_Departure _$DepartureFromJson(Map<String, dynamic> json) => _Departure(
stopId: json['stopId'] as String,
stopExtId: json['stopExtId'] as String?,
stopName: json['stopName'] as String,
name: json['name'] as String,
direction: json['direction'] as String,
directionFlag: json['directionFlag'] as String?,
scheduledTime: DateTime.parse(json['scheduledTime'] as String),
realTime: json['realTime'] == null
? null
: DateTime.parse(json['realTime'] as String),
delayMinutes: (json['delayMinutes'] as num?)?.toInt(),
track: json['track'] as String?,
realTrack: json['realTrack'] as String?,
cancelled: json['cancelled'] as bool? ?? false,
reachable: json['reachable'] as bool? ?? true,
product: json['product'] == null
? null
: Product.fromJson(json['product'] as Map<String, dynamic>),
journeyRef: json['journeyRef'] as String?,
);
Map<String, dynamic> _$DepartureToJson(_Departure instance) =>
<String, dynamic>{
'stopId': instance.stopId,
'stopExtId': instance.stopExtId,
'stopName': instance.stopName,
'name': instance.name,
'direction': instance.direction,
'directionFlag': instance.directionFlag,
'scheduledTime': instance.scheduledTime.toIso8601String(),
'realTime': instance.realTime?.toIso8601String(),
'delayMinutes': instance.delayMinutes,
'track': instance.track,
'realTrack': instance.realTrack,
'cancelled': instance.cancelled,
'reachable': instance.reachable,
'product': instance.product,
'journeyRef': instance.journeyRef,
};
_Arrival _$ArrivalFromJson(Map<String, dynamic> json) => _Arrival(
stopId: json['stopId'] as String,
stopExtId: json['stopExtId'] as String?,
stopName: json['stopName'] as String,
name: json['name'] as String,
origin: json['origin'] as String,
scheduledTime: DateTime.parse(json['scheduledTime'] as String),
realTime: json['realTime'] == null
? null
: DateTime.parse(json['realTime'] as String),
delayMinutes: (json['delayMinutes'] as num?)?.toInt(),
track: json['track'] as String?,
realTrack: json['realTrack'] as String?,
cancelled: json['cancelled'] as bool? ?? false,
product: json['product'] == null
? null
: Product.fromJson(json['product'] as Map<String, dynamic>),
journeyRef: json['journeyRef'] as String?,
);
Map<String, dynamic> _$ArrivalToJson(_Arrival instance) => <String, dynamic>{
'stopId': instance.stopId,
'stopExtId': instance.stopExtId,
'stopName': instance.stopName,
'name': instance.name,
'origin': instance.origin,
'scheduledTime': instance.scheduledTime.toIso8601String(),
'realTime': instance.realTime?.toIso8601String(),
'delayMinutes': instance.delayMinutes,
'track': instance.track,
'realTrack': instance.realTrack,
'cancelled': instance.cancelled,
'product': instance.product,
'journeyRef': instance.journeyRef,
};
_TripEndpoint _$TripEndpointFromJson(Map<String, dynamic> json) =>
_TripEndpoint(
stopId: json['stopId'] as String,
stopExtId: json['stopExtId'] as String?,
name: json['name'] as String,
lat: (json['lat'] as num?)?.toDouble(),
lon: (json['lon'] as num?)?.toDouble(),
scheduledTime: DateTime.parse(json['scheduledTime'] as String),
realTime: json['realTime'] == null
? null
: DateTime.parse(json['realTime'] as String),
delayMinutes: (json['delayMinutes'] as num?)?.toInt(),
track: json['track'] as String?,
realTrack: json['realTrack'] as String?,
type: json['type'] as String?,
);
Map<String, dynamic> _$TripEndpointToJson(_TripEndpoint instance) =>
<String, dynamic>{
'stopId': instance.stopId,
'stopExtId': instance.stopExtId,
'name': instance.name,
'lat': instance.lat,
'lon': instance.lon,
'scheduledTime': instance.scheduledTime.toIso8601String(),
'realTime': instance.realTime?.toIso8601String(),
'delayMinutes': instance.delayMinutes,
'track': instance.track,
'realTrack': instance.realTrack,
'type': instance.type,
};
_JourneyStop _$JourneyStopFromJson(Map<String, dynamic> json) => _JourneyStop(
id: json['id'] as String,
extId: json['extId'] as String?,
name: json['name'] as String,
lat: (json['lat'] as num?)?.toDouble(),
lon: (json['lon'] as num?)?.toDouble(),
routeIdx: (json['routeIdx'] as num?)?.toInt(),
scheduledArrival: json['scheduledArrival'] == null
? null
: DateTime.parse(json['scheduledArrival'] as String),
scheduledDeparture: json['scheduledDeparture'] == null
? null
: DateTime.parse(json['scheduledDeparture'] as String),
realArrival: json['realArrival'] == null
? null
: DateTime.parse(json['realArrival'] as String),
realDeparture: json['realDeparture'] == null
? null
: DateTime.parse(json['realDeparture'] as String),
arrTrack: json['arrTrack'] as String?,
depTrack: json['depTrack'] as String?,
realArrTrack: json['realArrTrack'] as String?,
realDepTrack: json['realDepTrack'] as String?,
cancelled: json['cancelled'] as bool? ?? false,
cancelledArrival: json['cancelledArrival'] as bool? ?? false,
cancelledDeparture: json['cancelledDeparture'] as bool? ?? false,
);
Map<String, dynamic> _$JourneyStopToJson(_JourneyStop instance) =>
<String, dynamic>{
'id': instance.id,
'extId': instance.extId,
'name': instance.name,
'lat': instance.lat,
'lon': instance.lon,
'routeIdx': instance.routeIdx,
'scheduledArrival': instance.scheduledArrival?.toIso8601String(),
'scheduledDeparture': instance.scheduledDeparture?.toIso8601String(),
'realArrival': instance.realArrival?.toIso8601String(),
'realDeparture': instance.realDeparture?.toIso8601String(),
'arrTrack': instance.arrTrack,
'depTrack': instance.depTrack,
'realArrTrack': instance.realArrTrack,
'realDepTrack': instance.realDepTrack,
'cancelled': instance.cancelled,
'cancelledArrival': instance.cancelledArrival,
'cancelledDeparture': instance.cancelledDeparture,
};
_Leg _$LegFromJson(Map<String, dynamic> json) => _Leg(
id: json['id'] as String,
idx: (json['idx'] as num).toInt(),
type: $enumDecodeNullable(_$LegTypeEnumMap, json['type']) ?? LegType.unknown,
name: json['name'] as String?,
category: json['category'] as String?,
number: json['number'] as String?,
direction: json['direction'] as String?,
origin: TripEndpoint.fromJson(json['origin'] as Map<String, dynamic>),
destination: TripEndpoint.fromJson(
json['destination'] as Map<String, dynamic>,
),
duration: IsoDuration.fromJson(json['duration'] as String?),
cancelled: json['cancelled'] as bool? ?? false,
partCancelled: json['partCancelled'] as bool? ?? false,
reachable: json['reachable'] as bool? ?? true,
product: json['product'] == null
? null
: Product.fromJson(json['product'] as Map<String, dynamic>),
journeyRef: json['journeyRef'] as String?,
stops:
(json['stops'] as List<dynamic>?)
?.map((e) => JourneyStop.fromJson(e as Map<String, dynamic>))
.toList() ??
const <JourneyStop>[],
);
Map<String, dynamic> _$LegToJson(_Leg instance) => <String, dynamic>{
'id': instance.id,
'idx': instance.idx,
'type': _$LegTypeEnumMap[instance.type]!,
'name': instance.name,
'category': instance.category,
'number': instance.number,
'direction': instance.direction,
'origin': instance.origin,
'destination': instance.destination,
'duration': IsoDuration.toJson(instance.duration),
'cancelled': instance.cancelled,
'partCancelled': instance.partCancelled,
'reachable': instance.reachable,
'product': instance.product,
'journeyRef': instance.journeyRef,
'stops': instance.stops,
};
const _$LegTypeEnumMap = {
LegType.journey: 'JOURNEY',
LegType.walk: 'WALK',
LegType.transfer: 'TRANSFER',
LegType.bike: 'BIKE',
LegType.car: 'CAR',
LegType.parkRide: 'PARK_RIDE',
LegType.taxi: 'TAXI',
LegType.checkIn: 'CHECK_IN',
LegType.checkOut: 'CHECK_OUT',
LegType.dummy: 'DUMMY',
LegType.unknown: 'UNKNOWN',
};
_Trip _$TripFromJson(Map<String, dynamic> json) => _Trip(
tripId: json['tripId'] as String?,
ctxRecon: json['ctxRecon'] as String?,
checksum: json['checksum'] as String?,
duration: IsoDuration.fromJson(json['duration'] as String?),
realDuration: IsoDuration.fromJson(json['realDuration'] as String?),
transferCount: (json['transferCount'] as num?)?.toInt(),
legs:
(json['legs'] as List<dynamic>?)
?.map((e) => Leg.fromJson(e as Map<String, dynamic>))
.toList() ??
const <Leg>[],
);
Map<String, dynamic> _$TripToJson(_Trip instance) => <String, dynamic>{
'tripId': instance.tripId,
'ctxRecon': instance.ctxRecon,
'checksum': instance.checksum,
'duration': IsoDuration.toJson(instance.duration),
'realDuration': IsoDuration.toJson(instance.realDuration),
'transferCount': instance.transferCount,
'legs': instance.legs,
};
_TripSearchResult _$TripSearchResultFromJson(Map<String, dynamic> json) =>
_TripSearchResult(
trips:
(json['trips'] as List<dynamic>?)
?.map((e) => Trip.fromJson(e as Map<String, dynamic>))
.toList() ??
const <Trip>[],
scrollContextLater: json['scrollContextLater'] as String?,
scrollContextEarlier: json['scrollContextEarlier'] as String?,
);
Map<String, dynamic> _$TripSearchResultToJson(_TripSearchResult instance) =>
<String, dynamic>{
'trips': instance.trips,
'scrollContextLater': instance.scrollContextLater,
'scrollContextEarlier': instance.scrollContextEarlier,
};
_JourneyDetail _$JourneyDetailFromJson(Map<String, dynamic> json) =>
_JourneyDetail(
journeyId: json['journeyId'] as String?,
product: json['product'] == null
? null
: Product.fromJson(json['product'] as Map<String, dynamic>),
direction: json['direction'] as String?,
stops:
(json['stops'] as List<dynamic>?)
?.map((e) => JourneyStop.fromJson(e as Map<String, dynamic>))
.toList() ??
const <JourneyStop>[],
);
Map<String, dynamic> _$JourneyDetailToJson(_JourneyDetail instance) =>
<String, dynamic>{
'journeyId': instance.journeyId,
'product': instance.product,
'direction': instance.direction,
'stops': instance.stops,
};
_HimMessage _$HimMessageFromJson(Map<String, dynamic> json) => _HimMessage(
id: json['id'] as String,
externalId: json['externalId'] as String?,
head: json['head'] as String?,
lead: json['lead'] as String?,
text: json['text'] as String?,
category: json['category'] as String?,
company: json['company'] as String?,
priority: (json['priority'] as num?)?.toInt(),
products: (json['products'] as num?)?.toInt(),
startValidity: json['startValidity'] == null
? null
: DateTime.parse(json['startValidity'] as String),
endValidity: json['endValidity'] == null
? null
: DateTime.parse(json['endValidity'] as String),
modified: json['modified'] == null
? null
: DateTime.parse(json['modified'] as String),
);
Map<String, dynamic> _$HimMessageToJson(_HimMessage instance) =>
<String, dynamic>{
'id': instance.id,
'externalId': instance.externalId,
'head': instance.head,
'lead': instance.lead,
'text': instance.text,
'category': instance.category,
'company': instance.company,
'priority': instance.priority,
'products': instance.products,
'startValidity': instance.startValidity?.toIso8601String(),
'endValidity': instance.endValidity?.toIso8601String(),
'modified': instance.modified?.toIso8601String(),
};