8 Commits
6 changed files with 102 additions and 31 deletions
+5 -3
View File
@@ -23,11 +23,13 @@ For the VBB network in Berlin/Brandenburg
this should work for other public transit networks using the HAFAS api, but you will have to test this yourself this should work for other public transit networks using the HAFAS api, but you will have to test this yourself
### The build part ### The build part
Create a `.env` in the project root and add Create a `.env` in the project root and add the following to it
```env ```env
VBB_API_KEY= VBB_API_KEY={YOUR_API_KEY}
VBB_API_URL= VBB_API_URL={YOUR_API_URL}
``` ```
For local builds and connected devices simply run `flutter run` in your terminal. For local builds and connected devices simply run `flutter run` in your terminal.
To build for linux: To build for linux:
+4 -10
View File
@@ -5,14 +5,7 @@ import 'dart:core';
import 'dart:convert'; import 'dart:convert';
import 'transport_helper.dart'; import 'transport_helper.dart';
import 'utilities.dart';
String timeOf(DateTime fullDT) {
// doing it this way so we always get two digits on the hour
String full = fullDT.toString();
List<String> splits = full.split(" ")[1].split(":");
String time = '${splits[0]}:${splits[1]}';
return time;
}
class ApiHandler { class ApiHandler {
// TODO: Split next line for better Error Handling // TODO: Split next line for better Error Handling
@@ -79,7 +72,7 @@ class ApiHandler {
static Future<List<Departure>> departures( static Future<List<Departure>> departures(
String stationId, { String stationId, {
int? maxDepartures = -1, int? maxDepartures = -1,
DateTime? time, DateTime? datetime,
int? duration = 0, int? duration = 0,
}) async { }) async {
String urlPath = '/api/fahrinfo/latest/departureBoard'; String urlPath = '/api/fahrinfo/latest/departureBoard';
@@ -88,8 +81,9 @@ class ApiHandler {
Uri url = Uri.https(baseUrl, urlPath, { Uri url = Uri.https(baseUrl, urlPath, {
'accessId': accessID, 'accessId': accessID,
'id': stationString, 'id': stationString,
'date': dateOf(datetime ?? DateTime.now()),
'time': timeOf( 'time': timeOf(
time ?? DateTime.now(), datetime ?? DateTime.now(),
), //time from which departures will be shown ), //time from which departures will be shown
'duration': duration.toString(), //interval time in minutes 'duration': duration.toString(), //interval time in minutes
'maxJourneys': maxDepartures.toString(), 'maxJourneys': maxDepartures.toString(),
+71 -13
View File
@@ -3,13 +3,17 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'api_handler.dart'; import 'api_handler.dart';
import 'transport_helper.dart'; import 'transport_helper.dart';
import 'dart:async'; import 'dart:async';
import 'dart:io';
// import 'package:sqflite_common_ffi/sqflite_ffi.dart'; //for testing on desktop import 'package:sqflite_common_ffi/sqflite_ffi.dart'; //for testing on desktop
import 'utilities.dart';
void main() async { void main() async {
await dotenv.load(); await dotenv.load();
const String dbName = "oeffimasterTransportDb"; const String dbName = "oeffimasterTransportDb";
// databaseFactory = databaseFactoryFfi; // for debugging and desktop test
if (Platform.isLinux) {
databaseFactory = databaseFactoryFfi; // for debugging and desktop test
}
DbHelper.initDb(dbName); DbHelper.initDb(dbName);
runApp(const MyApp()); runApp(const MyApp());
} }
@@ -177,7 +181,7 @@ class _ChooseStationPageState extends State<ChooseStationPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('RoutePicker')), appBar: AppBar(title: const Text('Start a Journey')),
body: Center( body: Center(
child: Column( child: Column(
children: [ children: [
@@ -191,7 +195,7 @@ class _ChooseStationPageState extends State<ChooseStationPage> {
builder: (BuildContext context, SearchController controller) { builder: (BuildContext context, SearchController controller) {
return SearchBar( return SearchBar(
controller: controller, controller: controller,
hintText: 'Search for a Station', hintText: 'Search your start Station',
padding: const WidgetStatePropertyAll<EdgeInsets>( padding: const WidgetStatePropertyAll<EdgeInsets>(
EdgeInsets.symmetric(horizontal: 16.0), EdgeInsets.symmetric(horizontal: 16.0),
), ),
@@ -292,7 +296,7 @@ class _ChooseDeparturePageState extends State<ChooseDeparturePage> {
void loadDepartures(Station startStation) async { void loadDepartures(Station startStation) async {
departureList = await ApiHandler.departures( departureList = await ApiHandler.departures(
startStation.id, startStation.id,
time: widget.arrival?.arrivalTime ?? DateTime.now(), datetime: widget.arrival?.arrivalTime ?? DateTime.now(),
); );
setState(() {}); setState(() {});
} }
@@ -320,12 +324,27 @@ class _ChooseDeparturePageState extends State<ChooseDeparturePage> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
'RoutePicker for ${widget.startStation.name} // ${widget.journeyId}', 'Departures for ${widget.startStation.name}',
), ),
), ),
body: Center( body: Center(
child: Column( child: Column(
children: [ children: [
Builder(
builder: (context) {
if (widget.arrival != null) {
return ListTile(
title: Text(
'arrive at ${widget.arrival?.arrivalTime}',
),
);
} else {
return ListTile(
title: Text(''),
);
}
},
),
Expanded( Expanded(
child: SizedBox( child: SizedBox(
height: 400, height: 400,
@@ -436,7 +455,7 @@ class _ChooseArrivalPageState extends State<ChooseArrivalPage> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
'Arrivals for ${widget.departure.vehicleName}// ${widget.journeyId}', 'Arrivals for ${widget.departure.vehicleName}',
), ),
), ),
body: Center( body: Center(
@@ -541,7 +560,16 @@ class _JourneyDetailPageState extends State<JourneyDetailPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text('Connection Details \n$duration')), appBar: AppBar(
title: ListTile(
title: Text(
'${widget.singleJourney.startStation}${widget.singleJourney.endStation}',
),
subtitle: Text(
'${widget.singleJourney.duration.toString().split(".")[0]}h',
),
),
),
body: Center( body: Center(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
@@ -556,7 +584,7 @@ class _JourneyDetailPageState extends State<JourneyDetailPage> {
margin: const EdgeInsets.only(bottom: 60.0), margin: const EdgeInsets.only(bottom: 60.0),
child: ElevatedButton( child: ElevatedButton(
onPressed: () { onPressed: () {
Navigator.pop( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => JourneyOverviewPage(), builder: (context) => JourneyOverviewPage(),
@@ -586,9 +614,39 @@ class SegmentList extends StatelessWidget {
return ListView( return ListView(
children: [ children: [
for (Segment seg in journey.segments) for (Segment seg in journey.segments)
Card(
child: Column(
mainAxisSize: .min,
children: [
Container(
padding: EdgeInsets.only(left: 10, top: 10),
child: Row(
children: [
Text(
seg.vehicle.name,
textScaler: TextScaler.linear(1.1),
),
Icon(Icons.arrow_right_alt),
Text('${seg.start.direction}'),
],
),
),
ListTile( ListTile(
title: Text(seg.start.direction), leading: Text(
subtitle: Text(seg.start.departureTime.toString()), timeOf(seg.start.departureTime),
textScaler: TextScaler.linear(1.4),
),
title: Text(seg.start.stationName),
),
ListTile(
leading: Text(
timeOf(seg.end.arrivalTime),
textScaler: TextScaler.linear(1.4),
),
title: Text(seg.end.stationName),
),
],
),
), ),
], ],
); );
@@ -641,7 +699,7 @@ class _JourneyOverviewPageState extends State<JourneyOverviewPage> {
height: 100.0, height: 100.0,
child: FittedBox( child: FittedBox(
fit: BoxFit.contain, fit: BoxFit.contain,
alignment: Alignment.centerLeft, alignment: Alignment.center,
child: Text("Your Journeys"), child: Text("Your Journeys"),
), ),
), ),
+6 -4
View File
@@ -1,4 +1,5 @@
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
import 'utilities.dart';
// import './state_helper.dart'; // import './state_helper.dart';
// import './db_helper.dart'; // import './db_helper.dart';
@@ -94,13 +95,15 @@ class Journey {
Map<String, Object?> journeyMaps, Map<String, Object?> journeyMaps,
List<Segment> segmentMaps, List<Segment> segmentMaps,
) { ) {
return Journey( Journey journey = Journey(
id: journeyMaps["id"] as int, id: journeyMaps["id"] as int,
startStation: journeyMaps["startstation"] as String?, startStation: journeyMaps["startstation"] as String?,
endStation: journeyMaps["endstation"] as String?, endStation: journeyMaps["endstation"] as String?,
segments: segmentMaps, segments: segmentMaps,
duration: Duration(minutes: 0), duration: Duration(minutes: 0),
); );
journey.sync();
return journey;
} }
// from trip API request // from trip API request
@@ -134,8 +137,8 @@ class Journey {
if (segments.isNotEmpty) { if (segments.isNotEmpty) {
startStation = segments.first.start.stationName; startStation = segments.first.start.stationName;
endStation = segments.last.end.stationName; endStation = segments.last.end.stationName;
duration = segments.first.start.departureTime.difference( duration = segments.last.end.arrivalTime.difference(
segments.last.end.arrivalTime, segments.first.start.departureTime,
); );
} else { } else {
// if the journey has no segments yet, at least the startStation should remain, // if the journey has no segments yet, at least the startStation should remain,
@@ -223,7 +226,6 @@ class Journey {
static Future<Journey> fromMapWrapper(Map<String, Object?> journeyMap) async { static Future<Journey> fromMapWrapper(Map<String, Object?> journeyMap) async {
Future<List<Segment>> segs = _dbGetJourneySegments(journeyMap["id"] as int); Future<List<Segment>> segs = _dbGetJourneySegments(journeyMap["id"] as int);
Journey res = Journey.fromMap(journeyMap, await segs); Journey res = Journey.fromMap(journeyMap, await segs);
res.sync();
return res; return res;
} }
+13
View File
@@ -0,0 +1,13 @@
String timeOf(DateTime fullDT) {
// doing it this way so we always get two digits on the hour
String full = fullDT.toString();
List<String> splits = full.split(" ")[1].split(":");
String time = '${splits[0]}:${splits[1]}';
return time;
}
String dateOf(DateTime fullDT) {
String full = fullDT.toString();
String date = full.split(" ")[0];
return date;
}
+2
View File
@@ -37,6 +37,8 @@ dependencies:
flutter_dotenv: ^6.0.1 flutter_dotenv: ^6.0.1
http: ^1.6.0 http: ^1.6.0
sqflite: ^2.4.2+1 sqflite: ^2.4.2+1
sqlite3: ^3.3.0
sqflite_common_ffi: ^2.4.0+3
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter