From 57481501298a0120da687c99bf748936f4649a14 Mon Sep 17 00:00:00 2001 From: Dan Cojocaru Date: Mon, 16 Sep 2019 00:03:31 +0300 Subject: [PATCH] Feature parity between iOS and Android --- CHANGELOG.TXT | 4 + ...in_info_cupertino_DisplayTrainStation.dart | 38 -- lib/train_info_page/train_info_material.dart | 225 +------- ...ain_info_material_DisplayTrainStation.dart | 509 ++++++++++++++++++ pubspec.yaml | 2 +- 5 files changed, 516 insertions(+), 262 deletions(-) create mode 100644 lib/train_info_page/train_info_material_DisplayTrainStation.dart diff --git a/CHANGELOG.TXT b/CHANGELOG.TXT index 0f3f195..3d1aef6 100644 --- a/CHANGELOG.TXT +++ b/CHANGELOG.TXT @@ -1,3 +1,7 @@ +v2.0.6 +Brought feature parity with iOS _(except for v2.0.2, which is iOS specific)_. + + v2.0.5 - increased font weight on iOS - added support for system bolt font request on iOS diff --git a/lib/train_info_page/train_info_cupertino_DisplayTrainStation.dart b/lib/train_info_page/train_info_cupertino_DisplayTrainStation.dart index bab4a13..bf6b360 100644 --- a/lib/train_info_page/train_info_cupertino_DisplayTrainStation.dart +++ b/lib/train_info_page/train_info_cupertino_DisplayTrainStation.dart @@ -207,44 +207,6 @@ class Time extends StatelessWidget { Container(width: 2,), ArrivalTime(station: station,), Expanded(child: Container(),), -// Column( -// mainAxisSize: MainAxisSize.min, -// children: [ -// Builder( -// builder: (context) { -// if (items[1].isEmpty || items[1] == "0") { -// return Container(); -// } -// if (items[1] == "1") { -// return Text( -// "staționează pentru\n1 minut", -// textAlign: TextAlign.center, -// ); -// } -// return Text( -// "staționează pentru\n${items[1]} minute", -// textAlign: TextAlign.center, -// ); -// } -// ), -// FutureBuilder( -// future: station.observations, -// builder: (context, snapshot) { -// if (snapshot.data == "ONI") { -// return Text( -// "oprire ne-itinerarică", -// style: CupertinoTheme.of(context).textTheme.textStyle.copyWith( -// fontStyle: FontStyle.italic, -// ), -// textAlign: TextAlign.center, -// ); -// } -// -// return Container(); -// }, -// ) -// ], -// ), StopTime(station: station,), Expanded(child: Container(),), DepartureTime(station: station,), diff --git a/lib/train_info_page/train_info_material.dart b/lib/train_info_page/train_info_material.dart index f660e85..6c414fe 100644 --- a/lib/train_info_page/train_info_material.dart +++ b/lib/train_info_page/train_info_material.dart @@ -1,4 +1,5 @@ import 'package:info_tren/train_info_page/train_info_animation_helpers.dart'; +import 'package:info_tren/train_info_page/train_info_material_DisplayTrainStation.dart'; import 'package:info_tren/utils/stream_list.dart'; import '../models/train_data.dart'; @@ -881,228 +882,6 @@ class DisplayTrainStations extends StatelessWidget { } } -class DisplayTrainStation extends StatelessWidget { - final OnDemandStation station; - - DisplayTrainStation({@required this.station}); - - @override - Widget build(BuildContext context) { - return Card( - child: Padding( - padding: const EdgeInsets.all(2), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisSize: MainAxisSize.max, - children: [ - Padding( - padding: const EdgeInsets.all(8), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - border: Border.all( - width: 2, - color: Colors.white70, - ), - // color: CupertinoColors.activeOrange, - ), - width: isSmallScreen(context) ? 42 : 48, - height: isSmallScreen(context) ? 42 : 48, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: Center( - child: FutureDisplay( - future: station.km, - builder: (context, value) { - return Text( - value.toString(), - style: Theme.of(context).textTheme.body1.copyWith( - fontSize: isSmallScreen(context) ? 14 : 18, - fontWeight: FontWeight.w100, - ), - textAlign: TextAlign.center, - ); - }, - ), - ), - ), - Text( - "km", - style: Theme.of(context).textTheme.body1.copyWith(fontSize: 10), - ), - ], - ), - ), - ), - Expanded( - child: FutureDisplay>( - future: Future.wait([ - station.stationName, - station.observations - ]), - builder: (context, items) { - return Text( - items[0], - style: Theme.of(context).textTheme.body1.copyWith( - fontSize: isSmallScreen(context) ? 18 : 22, - fontWeight: FontWeight.w100, - fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal, - ), - textAlign: TextAlign.center, - ); - }, - ) - ) - ], - ), - FutureDisplay>( - future: Future.wait([ - station.arrivalTime, - station.stopsFor, - station.departureTime - ]), - builder: (context, items) { - if (items[0].isEmpty) { - // Plecare - return Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded(child: Container(),), - Text("plecare la ${items[2]}"), - Container(width: 2,), - Text( - "→", - style: Theme.of(context).textTheme.body1.copyWith( - fontSize: isSmallScreen(context) ? 18 : 22, - ), - ), - ], - ); - } - - if (items[2].isEmpty) { - // Sosire - return Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - "→", - style: Theme.of(context).textTheme.body1.copyWith( - fontSize: isSmallScreen(context) ? 18 : 22, - ), - ), - Container(width: 2,), - Text("sosire la ${items[0]}"), - Expanded(child: Container(),), - ], - ); - } - - return Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - "→", - style: Theme.of(context).textTheme.body1.copyWith( - fontSize: isSmallScreen(context) ? 18 : 22, - ), - ), - Container(width: 2,), - Text(items[0]), - Expanded(child: Container(),), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - Builder( - builder: (context) { - if (items[1].isEmpty || items[1] == "0") { - return Container(); - } - if (items[1] == "1") { - return Text( - "staționează pentru\n1 minut", - textAlign: TextAlign.center, - ); - } - return Text( - "staționează pentru\n${items[1]} minute", - textAlign: TextAlign.center, - ); - } - ), - FutureBuilder( - future: station.observations, - builder: (context, snapshot) { - if (snapshot.data == "ONI") { - return Text( - "oprire ne-itinerarică", - style: Theme.of(context).textTheme.body1.copyWith( - fontStyle: FontStyle.italic, - ), - textAlign: TextAlign.center, - ); - } - - return Container(); - }, - ) - ], - ), - Expanded(child: Container(),), - Text(items[2]), - Container(width: 2,), - Text( - "→", - style: Theme.of(context).textTheme.body1.copyWith( - fontSize: isSmallScreen(context) ? 18 : 22, - ), - ), - ], - ); - }, - ), - FutureDisplay( - future: station.delay, - builder: (context, delay) { - if (delay == 0) return Container(); - - else if (delay > 0) { - return Text( - "$delay minute întârziere", - style: Theme.of(context).textTheme.body1.copyWith( - color: Colors.red, - fontSize: 12, - fontStyle: FontStyle.italic, - ), - ); - } - - else if (delay < 0) { - return Text( - "${-delay} minute mai devreme", - style: Theme.of(context).textTheme.body1.copyWith( - color: Colors.green, - fontSize: 12, - fontStyle: FontStyle.italic, - ), - ); - } - - return Container(); - }, - ) - ], - ), - ), - ); - } -} - class SlimAppBar extends StatelessWidget { final String title; final double size; @@ -1162,4 +941,4 @@ class SlimAppBar extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/train_info_page/train_info_material_DisplayTrainStation.dart b/lib/train_info_page/train_info_material_DisplayTrainStation.dart new file mode 100644 index 0000000..2f95e07 --- /dev/null +++ b/lib/train_info_page/train_info_material_DisplayTrainStation.dart @@ -0,0 +1,509 @@ +import 'package:flutter/material.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_material.dart' show isSmallScreen; + +class DisplayTrainStation extends StatelessWidget { + final OnDemandStation station; + + DisplayTrainStation({@required this.station}); + + @override + Widget build(BuildContext context) { + return Card( + child: Padding( + padding: const EdgeInsets.all(2), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisSize: MainAxisSize.max, + children: [ + FutureDisplay( + future: Future.wait([ + station.delay, + station.realOrEstimate, + station.observations, + ]), + builder: (context, data) { + final isDelayed = (data[0] as int) > 0 && (data[1] as RealOrEstimate) == RealOrEstimate.real; + final isOnTime = (data[0] as int) <= 0 && (data[1] as RealOrEstimate) == RealOrEstimate.real; + final isNotScheduled = data[2] == "ONI"; + + return KmBadge( + station: station, + isNotScheduled: isNotScheduled, + isDelayed: isDelayed, + isOnTime: isOnTime, + ); + } + ), + Expanded( + child: Title( + station: station, + ), + ), + ], + ), + Time( + station: station, + ), + Delay( + station: station, + ), + ], + ), + ), + ); + } +} + +class KmBadge extends StatelessWidget { + final OnDemandStation station; + final bool isNotScheduled; + final bool isOnTime; + final bool isDelayed; + + KmBadge({ + @required this.station, + this.isNotScheduled = false, + this.isOnTime = false, + this.isDelayed = false, + }); + + @override + Widget build(BuildContext context) { + Color foregroundColor = Colors.white70; + Color backgroundColor; + + if (isNotScheduled) { + foregroundColor = Colors.orange.shade300; + backgroundColor = Colors.orange.shade900.withOpacity(0.3); + } + else if (isOnTime) { + foregroundColor = Colors.green.shade300; + backgroundColor = Colors.green.shade900.withOpacity(0.3); + } + else if (isDelayed) { + foregroundColor = Colors.red.shade300; + backgroundColor = Colors.red.shade900.withOpacity(0.3); + } + + return Padding( + padding: const EdgeInsets.all(8), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + border: Border.all( + width: 2, + color: foregroundColor, + ), + color: backgroundColor, + ), + width: isSmallScreen(context) ? 42 : 48, + height: isSmallScreen(context) ? 42 : 48, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Center( + child: FutureDisplay( + future: station.km, + builder: (context, value) { + return Text( + value.toString(), + style: Theme.of(context).textTheme.body1.copyWith( + fontSize: isSmallScreen(context) ? 14 : 18, + fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200, + color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor, + ), + textAlign: TextAlign.center, + ); + }, + ), + ), + ), + Text( + "km", + style: Theme.of(context).textTheme.body1.copyWith( + fontSize: 10, + color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor, + ), + ), + ], + ), + ), + ); + } +} + +class Title extends StatelessWidget { + final OnDemandStation station; + + Title({ + @required this.station + }); + + @override + Widget build(BuildContext context) { + return FutureDisplay>( + future: Future.wait([ + station.stationName, + station.observations + ]), + builder: (context, items) { + return Text( + items[0], + style: Theme.of(context).textTheme.body1.copyWith( + fontSize: isSmallScreen(context) ? 18 : 22, + fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200, + fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal, + ), + textAlign: TextAlign.center, + ); + }, + ); + } +} + +class Time extends StatelessWidget { + final OnDemandStation station; + + Time({ + @required this.station, + }); + + @override + Widget build(BuildContext context) { + return FutureDisplay>( + future: Future.wait([ + station.arrivalTime, + station.stopsFor, + station.departureTime, + ]), + builder: (context, items) { + if (items[0].isEmpty) { + // Plecare + return DepartureTime( + station: station, + firstStation: true, + ); + } + + if (items[2].isEmpty) { + // Sosire + return ArrivalTime( + station: station, + finalStation: true, + ); + } + + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "→", + style: Theme.of(context).textTheme.body1.copyWith( + fontSize: isSmallScreen(context) ? 18 : 22, + ), + ), + Container(width: 2,), + ArrivalTime(station: station,), + Expanded(child: Container(),), + StopTime(station: station,), + Expanded(child: Container(),), + DepartureTime(station: station,), + Container(width: 2,), + Text( + "→", + style: Theme.of(context).textTheme.body1.copyWith( + fontSize: isSmallScreen(context) ? 18 : 22, + ), + ), + ], + ); + }, + ); + } +} + +class ArrivalTime extends StatelessWidget { + final OnDemandStation station; + final bool finalStation; + + ArrivalTime({ + @required this.station, + this.finalStation = false, + }); + + @override + Widget build(BuildContext context) { + return FutureDisplay>( + future: Future.wait([ + station.arrivalTime, + station.delay, + ]), + builder: (context, data) { + if (finalStation) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "→", + style: Theme.of(context).textTheme.body1.copyWith( + fontSize: isSmallScreen(context) ? 18 : 22, + ), + ), + Container(width: 2,), + Text("sosire la "), + ArrivalTime(station: station,), + Expanded(child: Container(),), + ], + ); + } + else { + if (data[1] == 0) { + return Text("${data[0]}"); + } + else if (data[1] as int > 0) { + final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList(); + + final now = DateTime.now(); + final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]); + final oldDate = newDate.subtract(Duration(minutes: data[1] as int)); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}", + style: Theme.of(context).textTheme.body1.copyWith( + decoration: TextDecoration.lineThrough, + ), + ), + Text( + "${data[0]}", + style: Theme.of(context).textTheme.body1.copyWith( + color: Colors.red.shade300, + ), + ), + ], + ); + } + else { + final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList(); + + final now = DateTime.now(); + final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]); + final oldDate = newDate.subtract(Duration(minutes: data[1] as int)); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}", + style: Theme.of(context).textTheme.body1.copyWith( + decoration: TextDecoration.lineThrough, + ), + ), + Text( + "${data[0]}", + style: Theme.of(context).textTheme.body1.copyWith( + color: Colors.green.shade300, + ), + ), + ], + ); + } + } + }, + ); + } +} + +class StopTime extends StatelessWidget { + final OnDemandStation station; + + StopTime({ + @required this.station, + }); + + @override + Widget build(BuildContext context) { + return FutureDisplay( + future: station.stopsFor, + builder: (context, stopsFor) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "staționează pentru", + textAlign: TextAlign.center, + ), + Builder( + builder: (context) { + int stopsForInt = int.parse(stopsFor); + if (stopsForInt == 1) { + return Text( + "1 minut", + textAlign: TextAlign.center, + ); + } + else if (stopsForInt < 20) { + return Text( + "$stopsFor minute", + textAlign: TextAlign.center, + ); + } + else { + return Text( + "$stopsFor de minute", + textAlign: TextAlign.center, + ); + } + }, + ) + ], + ); + }, + ); + } +} + +class DepartureTime extends StatelessWidget { + final OnDemandStation station; + final bool firstStation; + + DepartureTime({ + @required this.station, + this.firstStation = false, + }); + + @override + Widget build(BuildContext context) { + return FutureDisplay>( + future: Future.wait([ + station.departureTime, + station.delay, + ]), + builder: (context, data) { + if (firstStation) { + return Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Expanded(child: Container(),), + Text("plecare la "), + DepartureTime(station: station,), + Container(width: 2,), + Text( + "→", + style: Theme.of(context).textTheme.body1.copyWith( + fontSize: 22, + ), + ), + ], + ); + } + else { + if (data[1] == 0) { + return Text("${data[0]}"); + } + else if (data[1] as int > 0) { + final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList(); + + final now = DateTime.now(); + final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]); + final oldDate = newDate.subtract(Duration(minutes: data[1] as int)); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}", + style: Theme.of(context).textTheme.body1.copyWith( + decoration: TextDecoration.lineThrough, + ), + ), + Text( + "${data[0]}", + style: Theme.of(context).textTheme.body1.copyWith( + color: Colors.red.shade300, + ), + ), + ], + ); + } + else { + final splits = (data[0] as String).split(":").map((s) => int.parse(s)).toList(); + + final now = DateTime.now(); + final newDate = DateTime(now.year, now.month, now.day, splits[0], splits[1]); + final oldDate = newDate.subtract(Duration(minutes: data[1] as int)); + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + "${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}", + style: Theme.of(context).textTheme.body1.copyWith( + decoration: TextDecoration.lineThrough, + ), + ), + Text( + "${data[0]}", + style: Theme.of(context).textTheme.body1.copyWith( + color: Colors.green.shade300, + ), + ), + ], + ); + } + } + }, + ); + } +} + + +class Delay extends StatelessWidget { + final OnDemandStation station; + + Delay({ + @required this.station, + }); + + @override + Widget build(BuildContext context) { + return FutureDisplay( + future: station.delay, + builder: (context, delay) { + if (delay == 0) return Container(); + + else if (delay > 0) { + return Text( + "$delay minute întârziere", + style: Theme.of(context).textTheme.body1.copyWith( + color: Colors.red.shade300, + fontSize: 12, + fontStyle: FontStyle.italic, + ), + ); + } + + else if (delay < 0) { + return Text( + "${-delay} minute mai devreme", + style: Theme.of(context).textTheme.body1.copyWith( + color: Colors.green.shade300, + fontSize: 12, + fontStyle: FontStyle.italic, + ), + ); + } + + return Container(); + }, + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index ce5d787..d601ff1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,7 +11,7 @@ description: O aplicație de vizualizare a datelor puse la dispoziție de Inform # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 2.0.5 +version: 2.0.6 environment: sdk: ">=2.3.0 <3.0.0"