You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1022 lines
33 KiB

import 'package:flutter/cupertino.dart';
import 'package:info_tren/models/train_data.dart';
import 'package:info_tren/train_info_page/train_info.dart';
import 'package:info_tren/train_info_page/train_info_animation_helpers.dart';
import 'package:info_tren/train_info_page/train_info_constants.dart';
import 'package:info_tren/train_info_page/train_info_cupertino_DisplayTrainStation.dart';
import 'package:info_tren/utils/stream_list.dart';
class TrainInfoCupertino extends StatefulWidget {
final int trainNumber;
TrainInfoCupertino({@required this.trainNumber});
@override
_TrainInfoCupertinoState createState() => _TrainInfoCupertinoState();
}
class _TrainInfoCupertinoState extends State<TrainInfoCupertino> with TrainInfoMixin {
@override
void initState() {
super.initState();
title = widget.trainNumber.toString();
showTrainData = false;
requestedData = false;
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (!requestedData) {
requestedData = true;
TrainDataWebViewAdapter.of(context).loadTrain(widget.trainNumber).then((value) {
setState(() {
lookupResult = value;
});
if (lookupResult == TrainLookupResult.NOT_FOUND) {
Future.delayed(Duration(seconds: 5), () {
Navigator.of(context).pop();
});
}
else if (lookupResult == TrainLookupResult.FOUND) {
Future.delayed(Duration(seconds: 1, milliseconds: 500), () {
setState(() {
showTrainData = true;
});
});
}
});
}
}
@override
Widget build(BuildContext context) {
if (!showTrainData) {
return _TrainDataCupertinoBefore(
title: title,
lookupResult: lookupResult,
);
}
else {
return _TrainDataCupertinoAfter(
title: title,
);
}
}
}
class _TrainDataCupertinoBefore extends StatefulWidget {
final String title;
final TrainLookupResult lookupResult;
_TrainDataCupertinoBefore({
@required this.title,
@required this.lookupResult,
});
@override
__TrainDataCupertinoBeforeState createState() => __TrainDataCupertinoBeforeState();
}
class __TrainDataCupertinoBeforeState extends State<_TrainDataCupertinoBefore> {
@override
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text(widget.title ?? ""),
),
child: SafeArea(
child: StreamBuilder<ProgressReport>(
stream: TrainDataWebViewAdapter.of(context).progressStream,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
return Container();
case ConnectionState.waiting:
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
CupertinoActivityIndicator(),
Text(
"Conectare...",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
);
case ConnectionState.active:
break;
case ConnectionState.done:
Navigator.of(context).pop();
return Container();
}
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ProgressReportDisplayEntry(
key: ValueKey(1),
completed: 1 <= snapshot.data.current,
waitingText: "Se crează WebView",
completedText: "WebView a fost creat",
),
ProgressReportDisplayEntry(
key: ValueKey(2),
completed: 2 <= snapshot.data.current,
waitingText: "Se încarcă pagina Informatica Feroviară",
completedText: "Pagina Informatica Feroviară a fost încărcată",
),
ProgressReportDisplayEntry(
key: ValueKey(3),
completed: 3 <= snapshot.data.current,
waitingText: "Se încarcă informațiile despre tren",
completedText: "Informațiile despre tren au fost încărcate",
),
if (widget.lookupResult != null)
...[
Container(height: 20,),
SizedBox(
width: double.infinity,
child: AnimatedBackground(
animationDuration: Duration(milliseconds: 250),
initialColor: CupertinoTheme.of(context).scaffoldBackgroundColor,
backgroundColor:
widget.lookupResult == TrainLookupResult.FOUND
? BACKGROUND_GREEN
: BACKGROUND_RED,
child: Center(
child: Row(
children: <Widget>[
Expanded(child: Container(),),
if (widget.lookupResult == TrainLookupResult.FOUND)
Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 0, 8),
child: Center(
child: CupertinoActivityIndicator()
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
widget.lookupResult == TrainLookupResult.FOUND
? "Trenul a fost găsit"
: widget.lookupResult == TrainLookupResult.NOT_FOUND
? "Trenul nu a fost găsit"
: "A apărut o eroare în căutarea trenului",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(fontSize: 20),
),
),
Expanded(child: Container(),),
],
),
),
),
),
],
],
),
);
}
),
),
);
}
}
class _TrainDataCupertinoAfter extends StatefulWidget {
final String title;
_TrainDataCupertinoAfter({
@required this.title,
});
@override
__TrainDataCupertinoAfterState createState() => __TrainDataCupertinoAfterState();
}
class __TrainDataCupertinoAfterState extends State<_TrainDataCupertinoAfter> {
@override
Widget build(BuildContext context) {
return FutureBuilder<OnDemandTrainData>(
future: TrainDataWebViewAdapter.of(context).trainData(onInvalidation: () {
Navigator.of(context).pop();
}),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text(widget.title ?? ""),
),
child: SafeArea(
child: Center(
child: CupertinoActivityIndicator(),
),
),
);
}
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: FutureBuilder<List<String>>(
future: Future.wait([
snapshot.data.rang,
snapshot.data.trainNumber
]),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text("Informații despre ${snapshot.data[0]} ${snapshot.data[1]}");
}
else {
return Text(widget.title ?? "");
}
},
),
),
child: SafeArea(
top: false,
bottom: false,
child: Builder(
builder: (context) {
final topPadding = MediaQuery.of(context).padding.top;
return CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(
child: Padding(
padding: EdgeInsets.only(
top: topPadding,
),
child: Container(),
),
),
DisplayTrainID(trainData: snapshot.data,),
DisplayTrainOperator(trainData: snapshot.data,),
DisplayTrainRoute(trainData: snapshot.data,),
DisplayTrainDeparture(trainData: snapshot.data,),
SliverToBoxAdapter(
child: CupertinoDivider(
color: FOREGROUND_WHITE,
),
),
DisplayTrainLastInfo(trainData: snapshot.data,),
SliverToBoxAdapter(
child: CupertinoDivider(),
),
SliverToBoxAdapter(
child: IntrinsicHeight(
child: Row(
children: <Widget>[
Expanded(
child: DisplayTrainNextStop(trainData: snapshot.data,),
),
SizedBox(
height: double.infinity,
child: CupertinoVerticalDivider(),
),
Expanded(
child: DisplayTrainDestination(trainData: snapshot.data,),
)
],
),
),
),
SliverToBoxAdapter(
child: CupertinoDivider(),
),
SliverToBoxAdapter(
child: IntrinsicHeight(
child: Row(
children: <Widget>[
Expanded(
child: DisplayTrainRouteDuration(trainData: snapshot.data,),
),
SizedBox(
height: double.infinity,
child: CupertinoVerticalDivider(),
),
Expanded(
child: DisplayTrainRouteDistance(trainData: snapshot.data,),
)
],
),
),
),
SliverToBoxAdapter(
child: CupertinoDivider(
color: FOREGROUND_WHITE,
),
),
DisplayTrainStations(
trainData: snapshot.data,
pageLoadFuture: TrainDataWebViewAdapter.of(context).nextLoadFuture,
),
SliverToBoxAdapter(
child: Container(
height: MediaQuery.of(context).viewPadding.bottom,
),
),
],
);
}
),
),
);
},
);
// return CupertinoPageScaffold(
// navigationBar: CupertinoNavigationBar(
// middle: Text(title ?? ""),
// ),
// child: SafeArea(
// bottom: false,
// child: FutureBuilder<OnDemandTrainData>(
// future: TrainDataWebViewAdapter.of(context).trainData(onInvalidation: () {
// Navigator.of(context).pop();
// }),
// builder: (context, snapshot) {
// if (!snapshot.hasData) {
// return Center(
// child: CupertinoActivityIndicator(),
// );
// }
// try {
// Future.wait([
// snapshot.data.rang,
// snapshot.data.trainNumber
// ]).then((values) {
// setState(() {
// title = "Informații despre ${values[0]} ${values[1]}";
// });
// });
// return CustomScrollView(
// slivers: <Widget>[
// DisplayTrainID(data: snapshot.data,),
// DisplayTrainOperator(data: snapshot.data,),
// DisplayTrainRoute(data: snapshot.data,),
// DisplayTrainDeparture(data: snapshot.data,),
// SliverToBoxAdapter(
// child: CupertinoDivider(
// color: FOREGROUND_WHITE,
// ),
// ),
// DisplayTrainLastInfo(data: snapshot.data,),
// SliverToBoxAdapter(
// child: CupertinoDivider(),
// ),
// SliverToBoxAdapter(
// child: IntrinsicHeight(
// child: Row(
// children: <Widget>[
// Expanded(
// child: DisplayTrainNextStop(data: snapshot.data,),
// ),
// SizedBox(
// height: double.infinity,
// child: CupertinoVerticalDivider(),
// ),
// Expanded(
// child: DisplayTrainDestination(data: snapshot.data,),
// )
// ],
// ),
// ),
// ),
// SliverToBoxAdapter(
// child: CupertinoDivider(),
// ),
// SliverToBoxAdapter(
// child: IntrinsicHeight(
// child: Row(
// children: <Widget>[
// Expanded(
// child: DisplayTrainRouteDuration(data: snapshot.data,),
// ),
// SizedBox(
// height: double.infinity,
// child: CupertinoVerticalDivider(),
// ),
// Expanded(
// child: DisplayTrainRouteDistance(data: snapshot.data,),
// )
// ],
// ),
// ),
// ),
// SliverToBoxAdapter(
// child: CupertinoDivider(
// color: FOREGROUND_WHITE,
// ),
// ),
// DisplayTrainStations(
// data: snapshot.data,
// pageLoadFuture: TrainDataWebViewAdapter.of(context).nextLoadFuture,
// ),
// ],
// );
// }
// on OnDemandInvalidatedException {
// Navigator.of(context).pop();
// print("Got OnDemandInvalidatedException!");
// return Container();
// }
// },
// ),
// ),
// );
}
}
class DisplayTrainID extends StatelessWidget {
final OnDemandTrainData trainData;
DisplayTrainID({@required this.trainData});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: FutureDisplay(
future: Future.wait([
trainData.rang,
trainData.trainNumber
]),
builder: (context, datas) {
return Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"${datas[0]} ${datas[1]}",
style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
),
),
);
},
),
);
}
}
class DisplayTrainRoute extends StatelessWidget {
final OnDemandTrainData trainData;
DisplayTrainRoute({@required this.trainData});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: FutureDisplay(
future: Future.wait([trainData.route.from, trainData.route.to]),
builder: (context, routePieces) {
return Row(
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(
routePieces[0],
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16,
),
),
),
),
Expanded(child: Container(),),
Center(child: Text("-")),
Expanded(child: Container(),),
Center(
child: Padding(
padding: const EdgeInsets.all(4),
child: Text(
routePieces[1],
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16,
),
textAlign: TextAlign.right,
),
),
),
],
);
},
),
);
}
}
class DisplayTrainOperator extends StatelessWidget {
final OnDemandTrainData trainData;
DisplayTrainOperator({@required this.trainData});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: FutureDisplay(
future: trainData.operator,
builder: (context, operator) {
return Center(
child: Text(
operator,
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 14,
fontStyle: FontStyle.italic,
),
),
);
},
),
);
}
}
class DisplayTrainDeparture extends StatelessWidget {
final OnDemandTrainData trainData;
DisplayTrainDeparture({@required this.trainData});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.all(2),
child: FutureDisplay<DateTime>(
future: trainData.departureDate,
builder: (context, dataPlecare) {
return Text(
"Plecare în ${dataPlecare.day.toString().padLeft(2, '0')}.${dataPlecare.month.toString().padLeft(2, '0')}.${dataPlecare.year.toString().padLeft(4, '0')}",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontStyle: FontStyle.italic,
fontWeight: FontWeight.w200,
),
textAlign: TextAlign.center,
);
},
),
),
);
}
}
class DisplayTrainLastInfo extends StatelessWidget {
final OnDemandTrainData trainData;
DisplayTrainLastInfo({@required this.trainData});
@override
Widget build(BuildContext context) {
return SliverToBoxAdapter(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Center(
child: Padding(
padding: const EdgeInsets.all(2),
child: Text(
"Ultima informație",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
),
Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.all(4),
child: FutureDisplay(
future: trainData.lastInfo.station,
builder: (context, station) {
return Text(
station,
style: CupertinoTheme.of(context).textTheme.textStyle,
textAlign: TextAlign.left,
);
},
),
),
Expanded(child: Container(),),
Padding(
padding: const EdgeInsets.all(4),
child: FutureDisplay(
future: trainData.lastInfo.event,
builder: (context, event) {
return Text(
event,
style: CupertinoTheme.of(context).textTheme.textStyle,
textAlign: TextAlign.right,
);
},
),
),
],
),
FutureDisplay<DateTime>(
future: trainData.lastInfo.dateAndTime,
builder: (context, dt) {
return Text(
"Raportat în ${dt.day.toString().padLeft(2, '0')}.${dt.month.toString().padLeft(2, '0')}.${dt.year.toString().padLeft(4, '0')}, la ${dt.hour.toString().padLeft(2, '0')}:${dt.minute.toString().padLeft(2, '0')}",
textAlign: TextAlign.center,
);
},
),
FutureBuilder(
initialData: 0,
future: trainData.lastInfo.delay,
builder: (context, snapshot) {
if (snapshot.data == 0) {
return Container();
}
if (snapshot.data > 0) {
return Text(
"${snapshot.data} minute întârziere",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 14,
color: Color.fromRGBO(200, 30, 15, 1),
),
);
}
else {
return Text(
"${-snapshot.data} minute mai devreme",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 12,
color: Color.fromRGBO(15, 200, 15, 1),
),
);
}
},
)
],
),
);
}
}
class DisplayTrainNextStop extends StatelessWidget {
final OnDemandTrainData trainData;
DisplayTrainNextStop({@required this.trainData});
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: trainData.nextStop.stationName,
builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(4),
child: Text(
"Următoarea oprire",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 20,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
CupertinoDivider(
color: Color.fromRGBO(15, 15, 15, 1),
),
FutureDisplay(
future: trainData.nextStop.stationName,
builder: (context, station) {
return Padding(
padding: const EdgeInsets.fromLTRB(4, 0, 4, 0),
child: Text(
station,
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 18,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
);
},
),
FutureDisplay<DateTime>(
future: trainData.nextStop.arrival,
builder: (context, arrival) {
const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"];
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
"în ${arrival.day} ${months[arrival.month - 1]} ${arrival.year}",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 14,
),
textAlign: TextAlign.center,
),
Text(
"la ${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 14,
),
textAlign: TextAlign.center,
),
],
);
},
)
],
);
}
);
}
}
class DisplayTrainDestination extends StatelessWidget {
final OnDemandTrainData trainData;
DisplayTrainDestination({@required this.trainData});
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: trainData.destination.stationName,
builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(4),
child: Text(
"Destinația",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 20,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
),
CupertinoDivider(
color: Color.fromRGBO(15, 15, 15, 1),
),
FutureDisplay(
future: trainData.destination.stationName,
builder: (context, station) {
return Padding(
padding: const EdgeInsets.fromLTRB(4, 0, 4, 0),
child: Text(
station,
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 18,
fontWeight: FontWeight.w500,
),
textAlign: TextAlign.center,
),
);
},
),
FutureDisplay<DateTime>(
future: trainData.destination.arrival,
builder: (context, arrival) {
const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"];
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
"în ${arrival.day} ${months[arrival.month - 1]} ${arrival.year}",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 14,
),
textAlign: TextAlign.center,
),
Text(
"la ${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 14,
),
textAlign: TextAlign.center,
),
],
);
},
)
],
);
}
);
}
}
class DisplayTrainRouteDistance extends StatelessWidget {
final OnDemandTrainData trainData;
DisplayTrainRouteDistance({@required this.trainData});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
"Distanța rutei",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 18,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
FutureDisplay(
future: trainData.routeDistance,
builder: (context, distance) {
return Text(
"$distance km",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16,
),
textAlign: TextAlign.center,
);
},
),
],
);
}
}
class DisplayTrainRouteDuration extends StatelessWidget {
final OnDemandTrainData trainData;
DisplayTrainRouteDuration({@required this.trainData});
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
"Durata rutei",
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 18,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
FutureDisplay<Duration>(
future: trainData.routeDuration,
builder: (context, duration) {
var durationString = StringBuffer();
bool firstWritten = false;
if (duration.inDays > 0) {
firstWritten = true;
if (duration.inDays == 1) durationString.write("1 zi");
else durationString.write("${duration.inDays} zile");
duration -= Duration(days: duration.inDays);
}
if (duration.inHours > 0) {
if (firstWritten) {
durationString.write(", ");
}
firstWritten = true;
if (duration.inHours == 1) durationString.write("1 oră");
else durationString.write("${duration.inHours} ore");
duration -= Duration(hours: duration.inHours);
}
if (duration.inMinutes > 0) {
if (firstWritten) {
durationString.write(", ");
}
firstWritten = true;
if (duration.inMinutes == 1) durationString.write("1 minut");
else durationString.write("${duration.inMinutes} minute");
duration -= Duration(minutes: duration.inMinutes);
}
return Text(
durationString.toString(),
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 16,
),
textAlign: TextAlign.center,
);
},
),
],
);
}
}
class DisplayTrainStations extends StatelessWidget {
final OnDemandTrainData trainData;
final Future pageLoadFuture;
DisplayTrainStations({@required this.trainData, @required this.pageLoadFuture});
@override
Widget build(BuildContext context) {
return StreamBuilder<List<OnDemandStation>>(
stream: listifyStream(trainData.stations(pageLoadFuture: pageLoadFuture)),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return SliverToBoxAdapter(
child: Container(),
);
}
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index.isOdd) {
return CupertinoDivider();
}
else {
final itemIndex = index ~/ 2;
return IndexedSemantics(
child: DisplayTrainStation(
station: snapshot.data[itemIndex],
),
index: itemIndex,
);
}
},
childCount: snapshot.data.length * 2 - 1,
addSemanticIndexes: false,
),
);
},
);
}
}
class CupertinoDivider extends StatelessWidget {
final Color color;
CupertinoDivider({Key key, Color color}):
color = color ?? FOREGROUND_DARK_GREY,
super(key: key);
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
height: 1,
),
Container(
height: 1,
decoration: BoxDecoration(
color: color,
),
),
Container(
height: 1,
),
],
);
}
}
class CupertinoVerticalDivider extends StatelessWidget {
final Color color;
CupertinoVerticalDivider({Key key, Color color}):
color = color ?? FOREGROUND_DARK_GREY,
super(key: key);
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
width: 1,
),
Container(
width: 1,
decoration: BoxDecoration(
color: color,
),
),
Container(
width: 1,
),
],
);
}
}