Public Access
could query all, but the last 40 should be fine, no? could raise to no limit, but should be done as generator for that
550 lines
14 KiB
Dart
550 lines
14 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|
import 'api_handler.dart';
|
|
import 'transport_helper.dart';
|
|
|
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart'; //for testing on desktop
|
|
|
|
void main() async {
|
|
await dotenv.load();
|
|
const String dbName = "oeffimasterTransportDb";
|
|
databaseFactory = databaseFactoryFfi; // for debugging and desktop test
|
|
DbHelper.initDb(dbName);
|
|
runApp(const MyApp());
|
|
}
|
|
|
|
class MyApp extends StatelessWidget {
|
|
const MyApp({super.key});
|
|
|
|
// This widget is the root of your application.
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MaterialApp(
|
|
title: 'Oeffimaster',
|
|
theme: ThemeData(
|
|
colorScheme: .fromSeed(seedColor: Colors.yellow),
|
|
),
|
|
home: const MyHomePage(title: 'Oeffimaster'), //this is what matters
|
|
// routes: {
|
|
// //'/': (context) => const MyHomePage(title: 'Oeffimaster'),
|
|
// '/stations': (context) => const ChooseStationPage(),
|
|
// },
|
|
);
|
|
}
|
|
}
|
|
|
|
class MyHomePage extends StatefulWidget {
|
|
const MyHomePage({super.key, required this.title});
|
|
final String title;
|
|
|
|
@override
|
|
State<MyHomePage> createState() => _MyHomePageState();
|
|
}
|
|
|
|
class _MyHomePageState extends State<MyHomePage> {
|
|
int _counter = 0;
|
|
//String API_KEY = ApiHandler.accessID;
|
|
String API_KEY = "no telly";
|
|
//Future<Map<String, dynamic>> ?id;
|
|
List<Station> stationList = [];
|
|
String id = '';
|
|
|
|
void _incrementCounter() {
|
|
setState(() {
|
|
// This call to setState tells the Flutter framework that something has
|
|
// changed in this State, which causes it to rerun the build method below
|
|
// so that the display can reflect the updated values. If we changed
|
|
// _counter without calling setState(), then the build method would not be
|
|
// called again, and so nothing would appear to happen.
|
|
_counter++;
|
|
});
|
|
}
|
|
|
|
// TODO :find a better way to show the list
|
|
void showResult(Future<List<Station>> futureId) async {
|
|
stationList = await futureId;
|
|
id = stationList[0].id;
|
|
setState(() {});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// This method is rerun every time setState is called, for instance as done
|
|
// by the _incrementCounter method above.
|
|
//
|
|
// The Flutter framework has been optimized to make rerunning build methods
|
|
// fast, so that you can just rebuild anything that needs updating rather
|
|
// than having to individually change instances of widgets.
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
//backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
|
//backgroundColor: Colors.amber,
|
|
// Here we take the value from the MyHomePage object that was created by
|
|
// the App.build method, and use it to set our appbar title.
|
|
title: Text(widget.title),
|
|
),
|
|
body: Center(
|
|
child: Column(
|
|
mainAxisAlignment: .center,
|
|
children: [
|
|
const Text('You have pushed the button this many times:'),
|
|
Text(
|
|
'$_counter',
|
|
style: Theme.of(context).textTheme.headlineMedium,
|
|
),
|
|
Text('API key from ENV is $API_KEY'),
|
|
MaterialButton(
|
|
onPressed: () {
|
|
showResult(ApiHandler.findStations('S Biesdorf'));
|
|
},
|
|
child: Text('Search for \'S Biesdorf\''),
|
|
),
|
|
Text(id),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(builder: (context) => ChooseStationPage()),
|
|
);
|
|
},
|
|
child: const Text('Start a route'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
floatingActionButton: FloatingActionButton(
|
|
onPressed: _incrementCounter,
|
|
tooltip: 'Increment',
|
|
child: const Icon(Icons.add),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ChooseStationPage extends StatefulWidget {
|
|
const ChooseStationPage({super.key});
|
|
|
|
@override
|
|
State<ChooseStationPage> createState() => _ChooseStationPageState();
|
|
}
|
|
|
|
class _ChooseStationPageState extends State<ChooseStationPage> {
|
|
Station? _selectedStation;
|
|
List<Station> stationList = [];
|
|
|
|
void findNearbyStations() async {
|
|
stationList = await ApiHandler.nearbyStations(
|
|
// TODO: replace with location
|
|
"52.55",
|
|
"13.55",
|
|
maxStations: 20,
|
|
);
|
|
}
|
|
|
|
void findStationsByName(
|
|
String name, {
|
|
int? resultnum,
|
|
}) async {
|
|
stationList = await ApiHandler.findStations(name);
|
|
}
|
|
|
|
void showResult(Future<List<Station>> futureId) async {
|
|
stationList = await futureId;
|
|
setState(() {});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(title: const Text('RoutePicker')),
|
|
body: Center(
|
|
child: Column(
|
|
children: [
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
showResult(ApiHandler.findStations('S Biesdorf'));
|
|
},
|
|
child: Text('Search for \'S Biesdorf\''),
|
|
),
|
|
Expanded(
|
|
child: SizedBox(
|
|
height: 200,
|
|
child: StationList(
|
|
stationList: stationList,
|
|
onTapped: _handleStationTapped,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _handleStationTapped(Station station) {
|
|
// setState(() {
|
|
// _selectedStation = station;
|
|
// });
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => ChooseDeparturePage(startStation: station),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class StationList extends StatelessWidget {
|
|
final List<Station> stationList;
|
|
final ValueChanged<Station> onTapped;
|
|
|
|
StationList({
|
|
super.key,
|
|
required this.stationList,
|
|
required this.onTapped,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ListView(
|
|
children: [
|
|
for (Station station in stationList)
|
|
ListTile(
|
|
title: Text(station.name),
|
|
onTap: () => onTapped(station),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class ChooseDeparturePage extends StatefulWidget {
|
|
final Station startStation;
|
|
final int? journeyId;
|
|
const ChooseDeparturePage({
|
|
super.key,
|
|
this.journeyId,
|
|
required this.startStation,
|
|
});
|
|
|
|
@override
|
|
State<ChooseDeparturePage> createState() => _ChooseDeparturePageState();
|
|
}
|
|
|
|
class _ChooseDeparturePageState extends State<ChooseDeparturePage> {
|
|
List<Departure> departureList = [];
|
|
Journey? singleJourney;
|
|
|
|
void loadDepartures(Station startStation) async {
|
|
departureList = await ApiHandler.departures(startStation.id);
|
|
setState(() {});
|
|
}
|
|
|
|
void loadJourney(int journeyId) async {
|
|
singleJourney = await Journey.dbGet(journeyId);
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => JourneyDetailPage(
|
|
singleJourney: singleJourney as Journey,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
initState() {
|
|
super.initState();
|
|
loadDepartures(widget.startStation);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(
|
|
'RoutePicker for ${widget.startStation.name} // ${widget.journeyId}',
|
|
),
|
|
),
|
|
body: Center(
|
|
child: Column(
|
|
children: [
|
|
Expanded(
|
|
child: SizedBox(
|
|
height: 400,
|
|
child: DepartureList(
|
|
departureList: departureList,
|
|
onTapped: _handleDepartureTapped,
|
|
),
|
|
),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () {
|
|
if (widget.journeyId != null) {
|
|
loadJourney(widget.journeyId as int);
|
|
}
|
|
},
|
|
child: const Text('Finish Route'),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _handleDepartureTapped(Departure departure) {
|
|
// setState(() {
|
|
// _selectedDeparture = departure;
|
|
// });
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => ChooseArrivalPage(
|
|
departure: departure,
|
|
journeyId: widget.journeyId,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class DepartureList extends StatelessWidget {
|
|
final List<Departure> departureList;
|
|
final ValueChanged<Departure> onTapped;
|
|
|
|
const DepartureList({
|
|
super.key,
|
|
required this.departureList,
|
|
required this.onTapped,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ListView(
|
|
children: [
|
|
for (Departure departure in departureList)
|
|
ListTile(
|
|
title: Text('${departure.vehicleName}: ${departure.direction}'),
|
|
subtitle: Text(departure.departureTime.toString()),
|
|
onTap: () => onTapped(departure),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class ChooseArrivalPage extends StatefulWidget {
|
|
final Departure departure;
|
|
final int? journeyId;
|
|
|
|
const ChooseArrivalPage({
|
|
super.key,
|
|
this.journeyId,
|
|
required this.departure,
|
|
});
|
|
|
|
@override
|
|
State<ChooseArrivalPage> createState() => _ChooseArrivalPageState();
|
|
}
|
|
|
|
class _ChooseArrivalPageState extends State<ChooseArrivalPage> {
|
|
List<Departure> arrivalList = [];
|
|
Departure? _selectedDeparture;
|
|
|
|
void loadArrivals(String ref) async {
|
|
arrivalList = await ApiHandler.tripDetail(
|
|
ref,
|
|
startStationId: widget.departure.stationId,
|
|
);
|
|
setState(() {});
|
|
}
|
|
|
|
@override
|
|
initState() {
|
|
super.initState();
|
|
loadArrivals(widget.departure.ref);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(
|
|
'Arrivals for ${widget.departure.vehicleName}// ${widget.journeyId}',
|
|
),
|
|
),
|
|
body: Center(
|
|
child: Column(
|
|
children: [
|
|
Expanded(
|
|
child: SizedBox(
|
|
height: 400,
|
|
child: ArrivalList(
|
|
departure: widget.departure,
|
|
arrivalList: arrivalList,
|
|
onTapped: _handleArrivalTapped,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _handleArrivalTapped(Departure departure, Departure arrival) async {
|
|
// setState(() {
|
|
// _selectedDeparture = departure;
|
|
// });
|
|
departure.dbInsert;
|
|
arrival.dbInsert;
|
|
Segment newSegment = await Segment.fromDepartures(departure, arrival);
|
|
Station startStation = await Station.dbGet(arrival.stationId);
|
|
int journeyId;
|
|
if (widget.journeyId == null) {
|
|
Journey journey = await Journey.fromSegment(newSegment);
|
|
journeyId = journey.id as int;
|
|
} else {
|
|
Segment segment = newSegment;
|
|
Journey.dbAppendJourneySegment(
|
|
widget.journeyId as int,
|
|
segment.id as int,
|
|
);
|
|
journeyId = widget.journeyId as int;
|
|
}
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => ChooseDeparturePage(
|
|
startStation: startStation,
|
|
journeyId: journeyId,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class ArrivalList extends StatelessWidget {
|
|
final Departure departure;
|
|
final List<Departure> arrivalList;
|
|
final Function onTapped;
|
|
|
|
const ArrivalList({
|
|
super.key,
|
|
required this.departure,
|
|
required this.arrivalList,
|
|
required this.onTapped,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ListView(
|
|
children: [
|
|
for (Departure arrival in arrivalList)
|
|
ListTile(
|
|
title: Text(arrival.stationName),
|
|
subtitle: Text(arrival.arrivalTime.toString()),
|
|
onTap: () => onTapped(departure, arrival),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class JourneyDetailPage extends StatefulWidget {
|
|
final Journey singleJourney;
|
|
|
|
const JourneyDetailPage({
|
|
super.key,
|
|
required this.singleJourney,
|
|
});
|
|
|
|
@override
|
|
State<JourneyDetailPage> createState() => _JourneyDetailPageState();
|
|
}
|
|
|
|
class _JourneyDetailPageState extends State<JourneyDetailPage> {
|
|
Duration duration = Duration(minutes: 0);
|
|
|
|
@override
|
|
initState() {
|
|
super.initState();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
appBar: AppBar(title: Text('Connection Details \n$duration')),
|
|
body: Center(
|
|
child: SegmentList(journey: widget.singleJourney),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class SegmentList extends StatelessWidget {
|
|
final Journey journey;
|
|
|
|
const SegmentList({
|
|
super.key,
|
|
required this.journey,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ListView(
|
|
children: [
|
|
for (Segment seg in journey.segments)
|
|
ListTile(
|
|
title: Text(seg.start.direction),
|
|
subtitle: Text(seg.start.departureTime.toString()),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
class JourneyOverviewPage extends StatefulWidget {
|
|
const JourneyOverviewPage({super.key});
|
|
@override
|
|
State<JourneyOverviewPage> createState() => _JourneyOverviewPageState();
|
|
}
|
|
|
|
class _JourneyOverviewPageState extends State<JourneyOverviewPage> {
|
|
List<Journey> allJourneys = <Journey>[];
|
|
|
|
@override
|
|
initState() async {
|
|
super.initState();
|
|
// TODO: implement batch query to get a list (all) of journeys
|
|
allJourneys = await Journey.dbGetAll();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: Center(
|
|
child: JourneyList(journeys: allJourneys),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class JourneyList extends StatelessWidget {
|
|
final List<Journey> journeys;
|
|
const JourneyList({
|
|
super.key,
|
|
required this.journeys,
|
|
});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return ListView(
|
|
children: [
|
|
for (Journey jour in journeys)
|
|
ListTile(title: Text('${jour.startStation} to ${jour.endStation}')),
|
|
],
|
|
);
|
|
}
|
|
}
|