Browse Source

Transition to Material 3, main page redesign

master v2.7.6
Kenneth Bruen 2 years ago
parent
commit
7fcbdc18bd
Signed by: kbruen
GPG Key ID: C1980A470C3EE5B1
  1. 7
      CHANGELOG.txt
  2. 157
      lib/components/badge.dart
  3. 1
      lib/main.dart
  4. 5
      lib/pages/about/about_page_cupertino.dart
  5. 33
      lib/pages/about/about_page_material.dart
  6. 6
      lib/pages/main/main_page.dart
  7. 27
      lib/pages/main/main_page_material.dart
  8. 2
      lib/pages/station_arrdep_page/view_station/view_station.dart
  9. 32
      lib/pages/station_arrdep_page/view_station/view_station_material.dart
  10. 83
      lib/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart
  11. 262
      lib/pages/train_info_page/view_train/train_info_material.dart
  12. 215
      lib/pages/train_info_page/view_train/train_info_material_DisplayTrainStation.dart
  13. 2
      pubspec.yaml

7
CHANGELOG.txt

@ -1,3 +1,10 @@
v2.7.6
Transitioned to Material 3.
Redesigned main page on Material.
On Android (Material), tapping station card in train information screen opens departures/arrivals board.
Added past tense to trains already arrived/departed.
Fixed download button on Android.
v2.7.5 v2.7.5
Added about page and in-app changelog. Added about page and in-app changelog.
On Android, added download buttons. On Android, added download buttons.

157
lib/components/badge.dart

@ -0,0 +1,157 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:info_tren/pages/train_info_page/train_info_constants.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
class MaterialBadge extends StatelessWidget {
final String text;
final String caption;
final bool isNotScheduled;
final bool isOnTime;
final bool isDelayed;
MaterialBadge({
required this.text,
required this.caption,
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: <Widget>[
Expanded(
child: Center(
child: Text(
text,
style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 16 : 20,
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor,
),
textAlign: TextAlign.center,
),
),
),
Text(
caption,
style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: 10,
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor,
),
),
],
),
),
);
}
}
class CupertinoBadge extends StatelessWidget {
final String text;
final String caption;
final bool isNotScheduled;
final bool isOnTime;
final bool isDelayed;
CupertinoBadge({
required this.text,
required this.caption,
this.isNotScheduled = false,
this.isOnTime = false,
this.isDelayed = false,
});
@override
Widget build(BuildContext context) {
Color foregroundColor = FOREGROUND_WHITE;
Color? backgroundColor;
if (isNotScheduled) {
foregroundColor = Color.fromRGBO(225, 175, 30, 1);
backgroundColor = Color.fromRGBO(80, 40, 10, 1);
}
else if (isOnTime) {
foregroundColor = Color.fromRGBO(130, 175, 65, 1);
backgroundColor = Color.fromRGBO(40, 80, 10, 1);
}
else if (isDelayed) {
foregroundColor = Color.fromRGBO(225, 75, 30, 1);
backgroundColor = Color.fromRGBO(80, 20, 10, 1);
}
return Padding(
padding: const EdgeInsets.all(8),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
width: 2,
color: foregroundColor,
),
color: backgroundColor,
// color: CupertinoColors.activeOrange,
),
width: 48,
height: 48,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: Center(
child: Text(
text,
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 20,
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
color: MediaQuery.of(context).boldText ? FOREGROUND_WHITE : foregroundColor,
),
textAlign: TextAlign.center,
),
),
),
Text(
caption,
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 12,
color: MediaQuery.of(context).boldText ? FOREGROUND_WHITE : foregroundColor,
),
),
],
),
),
);
}
}

1
lib/main.dart

@ -97,6 +97,7 @@ class StartPoint extends StatelessWidget {
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
accentColor: Colors.blue.shade700, accentColor: Colors.blue.shade700,
), ),
useMaterial3: true,
// fontFamily: 'Atkinson Hyperlegible', // fontFamily: 'Atkinson Hyperlegible',
), ),
routes: routesByUiDesign(UiDesign.MATERIAL), routes: routesByUiDesign(UiDesign.MATERIAL),

5
lib/pages/about/about_page_cupertino.dart

@ -100,7 +100,10 @@ class AboutPageStateCupertino extends AboutPageState {
padding: EdgeInsets.all(4), padding: EdgeInsets.all(4),
minSize: 0, minSize: 0,
onPressed: () { onPressed: () {
launchUrl(log.apkLink!); launchUrl(
log.apkLink!,
mode: LaunchMode.externalApplication,
);
}, },
child: Icon(CupertinoIcons.arrow_down_circle), child: Icon(CupertinoIcons.arrow_down_circle),
), ),

33
lib/pages/about/about_page_material.dart

@ -1,4 +1,6 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:info_tren/pages/about/about_page.dart'; import 'package:info_tren/pages/about/about_page.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -85,12 +87,33 @@ class AboutPageStateMaterial extends AboutPageState {
), ),
), ),
if (AboutPageState.DOWNLOAD == 'apk' && log.apkLink != null) if (AboutPageState.DOWNLOAD == 'apk' && log.apkLink != null)
IconButton( GestureDetector(
onPressed: () { onSecondaryTap: () {
launchUrl(log.apkLink!); Clipboard.setData(ClipboardData(text: log.apkLink!.toString()));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Link copied to clipboard'),
));
}, },
icon: Icon(Icons.download), onLongPress: () {
tooltip: 'Download APK', Clipboard.setData(ClipboardData(text: log.apkLink!.toString()));
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Link copied to clipboard'),
));
},
onTap: () {
launchUrl(
log.apkLink!,
mode: LaunchMode.externalApplication,
);
},
behavior: HitTestBehavior.translucent,
child: Tooltip(
message: 'Download APK',
child: Padding(
padding: const EdgeInsets.all(4),
child: Icon(Icons.download),
),
),
), ),
], ],
), ),

6
lib/pages/main/main_page.dart

@ -43,18 +43,21 @@ abstract class MainPageShared extends StatelessWidget {
List<MainPageAction> get options => [ List<MainPageAction> get options => [
MainPageAction( MainPageAction(
name: 'Informații despre tren', name: 'Informații despre tren',
description: 'Află informații despre parcursul unui anumit tren',
action: (context) { action: (context) {
onTrainInfoPageInvoke(context); onTrainInfoPageInvoke(context);
}, },
), ),
MainPageAction( MainPageAction(
name: 'Tabelă plecari/sosiri', name: 'Tabelă plecari/sosiri',
description: 'Vezi trenurile care pleacă și sosesc dintr-o gară',
action: (context) { action: (context) {
onStationBoardPageInvoke(context); onStationBoardPageInvoke(context);
}, },
), ),
MainPageAction( MainPageAction(
name: 'Planificare rută', name: 'Planificare rută',
description: 'Găsește trenurile disponibile pentru călătoria între două gări',
// TODO: Implement route planning // TODO: Implement route planning
action: null, action: null,
), ),
@ -75,7 +78,8 @@ abstract class MainPageShared extends StatelessWidget {
class MainPageAction { class MainPageAction {
final String name; final String name;
final String? description;
final void Function(BuildContext context)? action; final void Function(BuildContext context)? action;
MainPageAction({required this.name, this.action}); MainPageAction({required this.name, this.action, this.description});
} }

27
lib/pages/main/main_page_material.dart

@ -26,12 +26,29 @@ class MainPageMaterial extends MainPageShared {
child: Center( child: Center(
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: options.map((option) => ElevatedButton( children: options.map((option) => Card(
child: Text( color: option.action != null ? Theme.of(context).colorScheme.secondaryContainer : null,
option.name, child: InkWell(
style: Theme.of(context).textTheme.button?.copyWith(fontSize: 18), onTap: option.action != null ? () => option.action!(context) : null,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
option.name,
style: Theme.of(context).textTheme.headline4?.copyWith(
color: Theme.of(context).colorScheme.onSecondaryContainer,
),
textAlign: TextAlign.center,
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(option.description!),
),
],
),
), ),
onPressed: option.action != null ? () => option.action!(context) : null,
)).map((w) => Padding( )).map((w) => Padding(
padding: const EdgeInsets.fromLTRB(4, 2, 4, 2), padding: const EdgeInsets.fromLTRB(4, 2, 4, 2),
child: SizedBox( child: SizedBox(

2
lib/pages/station_arrdep_page/view_station/view_station.dart

@ -33,7 +33,9 @@ abstract class ViewStationPageState extends State<ViewStationPage> {
static const departures = 'Pleacări'; static const departures = 'Pleacări';
static const loadingText = 'Se încarcă...'; static const loadingText = 'Se încarcă...';
static const arrivesFrom = 'Sosește de la'; static const arrivesFrom = 'Sosește de la';
static const arrivedFrom = 'A sosit de la';
static const departsTo = 'Pleacă către'; static const departsTo = 'Pleacă către';
static const departedTo = 'A plecat către';
ViewStationPageTab tab = ViewStationPageTab.departures; ViewStationPageTab tab = ViewStationPageTab.departures;
late String stationName; late String stationName;

32
lib/pages/station_arrdep_page/view_station/view_station_material.dart

@ -13,19 +13,23 @@ class ViewStationPageStateMaterial extends ViewStationPageState {
title: Text(snapshot.hasData ? snapshot.data!.stationName : stationName), title: Text(snapshot.hasData ? snapshot.data!.stationName : stationName),
centerTitle: true, centerTitle: true,
), ),
body: snapshot.state == RefreshFutureBuilderState.waiting ? Loading(text: ViewStationPageState.loadingText, uiDesign: widget.uiDesign,) : CustomScrollView( body: snapshot.state == RefreshFutureBuilderState.waiting
slivers: [ ? Loading(text: ViewStationPageState.loadingText, uiDesign: widget.uiDesign,)
SliverToBoxAdapter(child: SafeArea(child: Container(), left: false, bottom: false, right: false,),), : snapshot.state == RefreshFutureBuilderState.error
SliverList( ? Container()
delegate: SliverChildBuilderDelegate( : CustomScrollView(
(context, index) { slivers: [
return tab == ViewStationPageTab.arrivals ? buildStationArrivalItem(context, snapshot.data!.arrivals![index]) : buildStationDepartureItem(context, snapshot.data!.departures![index]); SliverToBoxAdapter(child: SafeArea(child: Container(), left: false, bottom: false, right: false,),),
}, SliverList(
childCount: tab == ViewStationPageTab.arrivals ? snapshot.data!.arrivals?.length ?? 0 : snapshot.data!.departures?.length ?? 0, delegate: SliverChildBuilderDelegate(
(context, index) {
return tab == ViewStationPageTab.arrivals ? buildStationArrivalItem(context, snapshot.data!.arrivals![index]) : buildStationDepartureItem(context, snapshot.data!.departures![index]);
},
childCount: tab == ViewStationPageTab.arrivals ? snapshot.data!.arrivals?.length ?? 0 : snapshot.data!.departures?.length ?? 0,
),
), ),
), ],
], ),
),
bottomNavigationBar: snapshot.hasData ? BottomNavigationBar( bottomNavigationBar: snapshot.hasData ? BottomNavigationBar(
items: [ items: [
BottomNavigationBarItem( BottomNavigationBarItem(
@ -64,7 +68,7 @@ class ViewStationPageStateMaterial extends ViewStationPageState {
subtitle: Text.rich( subtitle: Text.rich(
TextSpan( TextSpan(
children: [ children: [
TextSpan(text: ViewStationPageState.arrivesFrom), TextSpan(text: item.time.compareTo(DateTime.now()) < 0 ? ViewStationPageState.arrivedFrom : ViewStationPageState.arrivesFrom),
TextSpan(text: ' '), TextSpan(text: ' '),
TextSpan(text: item.train.origin), TextSpan(text: item.train.origin),
], ],
@ -95,7 +99,7 @@ class ViewStationPageStateMaterial extends ViewStationPageState {
subtitle: Text.rich( subtitle: Text.rich(
TextSpan( TextSpan(
children: [ children: [
TextSpan(text: ViewStationPageState.departsTo), TextSpan(text: item.time.compareTo(DateTime.now()) < 0 ? ViewStationPageState.departedTo : ViewStationPageState.departsTo),
TextSpan(text: ' '), TextSpan(text: ' '),
TextSpan(text: item.train.destination), TextSpan(text: item.train.destination),
], ],

83
lib/pages/train_info_page/view_train/train_info_cupertino_DisplayTrainStation.dart

@ -1,6 +1,6 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:info_tren/models/train_data.dart'; import 'package:info_tren/models/train_data.dart';
import 'package:info_tren/pages/train_info_page/train_info_constants.dart'; import 'package:info_tren/components/badge.dart';
class DisplayTrainStation extends StatelessWidget { class DisplayTrainStation extends StatelessWidget {
final Station station; final Station station;
@ -47,7 +47,7 @@ class DisplayTrainStation extends StatelessWidget {
final isOnTime = delay <= 0 && real == true; final isOnTime = delay <= 0 && real == true;
final isNotScheduled = false; final isNotScheduled = false;
return Badge( return CupertinoBadge(
text: station.km.toString(), text: station.km.toString(),
caption: 'km', caption: 'km',
isNotScheduled: isNotScheduled, isNotScheduled: isNotScheduled,
@ -65,7 +65,7 @@ class DisplayTrainStation extends StatelessWidget {
flex: 1, flex: 1,
child: Align( child: Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: station.platform == null ? Container() : Badge(text: station.platform!, caption: 'linia'), child: station.platform == null ? Container() : CupertinoBadge(text: station.platform!, caption: 'linia'),
), ),
), ),
], ],
@ -81,83 +81,6 @@ class DisplayTrainStation extends StatelessWidget {
} }
} }
class Badge extends StatelessWidget {
final String text;
final String caption;
final bool isNotScheduled;
final bool isOnTime;
final bool isDelayed;
Badge({
required this.text,
required this.caption,
this.isNotScheduled = false,
this.isOnTime = false,
this.isDelayed = false,
});
@override
Widget build(BuildContext context) {
Color foregroundColor = FOREGROUND_WHITE;
Color? backgroundColor;
if (isNotScheduled) {
foregroundColor = Color.fromRGBO(225, 175, 30, 1);
backgroundColor = Color.fromRGBO(80, 40, 10, 1);
}
else if (isOnTime) {
foregroundColor = Color.fromRGBO(130, 175, 65, 1);
backgroundColor = Color.fromRGBO(40, 80, 10, 1);
}
else if (isDelayed) {
foregroundColor = Color.fromRGBO(225, 75, 30, 1);
backgroundColor = Color.fromRGBO(80, 20, 10, 1);
}
return Padding(
padding: const EdgeInsets.all(8),
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
border: Border.all(
width: 2,
color: foregroundColor,
),
color: backgroundColor,
// color: CupertinoColors.activeOrange,
),
width: 48,
height: 48,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Expanded(
child: Center(
child: Text(
text,
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 20,
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
color: MediaQuery.of(context).boldText ? FOREGROUND_WHITE : foregroundColor,
),
textAlign: TextAlign.center,
),
),
),
Text(
caption,
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
fontSize: 12,
color: MediaQuery.of(context).boldText ? FOREGROUND_WHITE : foregroundColor,
),
),
],
),
),
);
}
}
class Title extends StatelessWidget { class Title extends StatelessWidget {
final Station station; final Station station;

262
lib/pages/train_info_page/view_train/train_info_material.dart

@ -3,18 +3,24 @@ import 'package:flutter/material.dart';
import 'package:info_tren/components/slim_app_bar.dart'; import 'package:info_tren/components/slim_app_bar.dart';
import 'package:info_tren/models/train_data.dart' hide State; import 'package:info_tren/models/train_data.dart' hide State;
import 'package:info_tren/models/ui_design.dart'; import 'package:info_tren/models/ui_design.dart';
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart'; import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_material_DisplayTrainStation.dart'; import 'package:info_tren/pages/train_info_page/view_train/train_info_material_DisplayTrainStation.dart';
import 'package:info_tren/utils/state_to_string.dart'; import 'package:info_tren/utils/state_to_string.dart';
class TrainInfoLoadingMaterial extends TrainInfoLoading { class TrainInfoLoadingMaterial extends TrainInfoLoading {
TrainInfoLoadingMaterial({required String title, String? loadingText}) : super(title: title, loadingText: loadingText, uiDesign: UiDesign.MATERIAL); TrainInfoLoadingMaterial({required String title, String? loadingText})
: super(
title: title,
loadingText: loadingText,
uiDesign: UiDesign.MATERIAL);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(title), title: Text(title),
centerTitle: true,
), ),
body: Center( body: Center(
child: loadingWidget, child: loadingWidget,
@ -28,7 +34,11 @@ class TrainInfoErrorMaterial extends TrainInfoError {
required Object error, required Object error,
required String title, required String title,
Future Function()? refresh, Future Function()? refresh,
}) : super(error: error, title: title, refresh: refresh,); }) : super(
error: error,
title: title,
refresh: refresh,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -56,24 +66,32 @@ class TrainInfoErrorMaterial extends TrainInfoError {
} }
} }
bool isSmallScreen(BuildContext context) => MediaQuery.of(context).size.height <= 425; bool isSmallScreen(BuildContext context) =>
MediaQuery.of(context).size.height <= 425;
class TrainInfoMaterial extends StatelessWidget { class TrainInfoMaterial extends StatelessWidget {
final TrainData trainData; final TrainData trainData;
final Future Function()? refresh; final Future Function()? refresh;
final void Function()? onViewYesterdayTrain; final void Function()? onViewYesterdayTrain;
TrainInfoMaterial({required this.trainData, this.refresh, this.onViewYesterdayTrain,}); TrainInfoMaterial({
required this.trainData,
this.refresh,
this.onViewYesterdayTrain,
});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Builder( return Builder(
builder: (context) { builder: (context) {
return Scaffold( return Scaffold(
appBar: isSmallScreen(context) ? null : AppBar( appBar: isSmallScreen(context)
centerTitle: true, ? null
title: Text("Informații despre ${trainData.rank} ${trainData.number}"), : AppBar(
), centerTitle: true,
title: Text(
"Informații despre ${trainData.rank} ${trainData.number}"),
),
body: Column( body: Column(
children: <Widget>[ children: <Widget>[
if (isSmallScreen(context)) if (isSmallScreen(context))
@ -82,8 +100,8 @@ class TrainInfoMaterial extends StatelessWidget {
left: false, left: false,
right: false, right: false,
child: SlimAppBar( child: SlimAppBar(
title: 'INFO TREN - ${trainData.rank} ${trainData.number}' title:
), 'INFO TREN - ${trainData.rank} ${trainData.number}'),
), ),
Expanded( Expanded(
child: SafeArea( child: SafeArea(
@ -94,19 +112,27 @@ class TrainInfoMaterial extends StatelessWidget {
child: CustomScrollView( child: CustomScrollView(
slivers: <Widget>[ slivers: <Widget>[
SliverToBoxAdapter( SliverToBoxAdapter(
child: DisplayTrainID(trainData: trainData,), child: DisplayTrainID(
trainData: trainData,
),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: DisplayTrainOperator(trainData: trainData,), child: DisplayTrainOperator(
trainData: trainData,
),
), ),
SliverPadding( SliverPadding(
padding: const EdgeInsets.only(left: 2, right: 2), padding: const EdgeInsets.only(left: 2, right: 2),
sliver: SliverToBoxAdapter( sliver: SliverToBoxAdapter(
child: DisplayTrainRoute(trainData: trainData,), child: DisplayTrainRoute(
trainData: trainData,
),
), ),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: DisplayTrainDeparture(trainData: trainData,), child: DisplayTrainDeparture(
trainData: trainData,
),
), ),
// SliverToBoxAdapter( // SliverToBoxAdapter(
// child: Divider( // child: Divider(
@ -115,7 +141,9 @@ class TrainInfoMaterial extends StatelessWidget {
// ), // ),
// ), // ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: DisplayTrainLastInfo(trainData: trainData,), child: DisplayTrainLastInfo(
trainData: trainData,
),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: IntrinsicHeight( child: IntrinsicHeight(
@ -123,8 +151,15 @@ class TrainInfoMaterial extends StatelessWidget {
children: <Widget>[ children: <Widget>[
// Expanded(child: DisplayTrainNextStop(trainData: trainData,)), // Expanded(child: DisplayTrainNextStop(trainData: trainData,)),
// Expanded(child: DisplayTrainDestination(trainData: trainData,)), // Expanded(child: DisplayTrainDestination(trainData: trainData,)),
Expanded(child: DisplayTrainRouteDuration(trainData: trainData,)), Expanded(
Expanded(child: DisplayTrainRouteDistance(trainData: trainData,),), child: DisplayTrainRouteDuration(
trainData: trainData,
)),
Expanded(
child: DisplayTrainRouteDistance(
trainData: trainData,
),
),
], ],
), ),
), ),
@ -146,9 +181,13 @@ class TrainInfoMaterial extends StatelessWidget {
height: isSmallScreen(context) ? 8 : 16, height: isSmallScreen(context) ? 8 : 16,
), ),
), ),
if (onViewYesterdayTrain != null && trainData.stations.first.departure!.scheduleTime.compareTo(DateTime.now()) > 0) ...[ if (onViewYesterdayTrain != null &&
trainData.stations.first.departure!.scheduleTime
.compareTo(DateTime.now()) >
0) ...[
SliverToBoxAdapter( SliverToBoxAdapter(
child: DisplayTrainYesterdayWarningMaterial(onViewYesterdayTrain!), child: DisplayTrainYesterdayWarningMaterial(
onViewYesterdayTrain!),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: Divider( child: Divider(
@ -191,19 +230,24 @@ class DisplayTrainID extends StatelessWidget {
TextSpan( TextSpan(
text: trainData.rank, text: trainData.rank,
style: TextStyle( style: TextStyle(
color: trainData.rank.startsWith('IR') ? Color.fromARGB(255, 255, 0, 0) : null, color: trainData.rank.startsWith('IR')
? Color.fromARGB(255, 255, 0, 0)
: null,
), ),
), ),
TextSpan(text: ' '), TextSpan(text: ' '),
TextSpan(text: trainData.number,), TextSpan(
text: trainData.number,
),
], ],
), ),
style: (isSmallScreen(context) style: (isSmallScreen(context)
? Theme.of(context).textTheme.headline4 ? Theme.of(context).textTheme.headline4
: Theme.of(context).textTheme.headline3)?.copyWith( : Theme.of(context).textTheme.headline3)
color: Theme.of(context).textTheme.bodyText2?.color, ?.copyWith(
fontWeight: FontWeight.bold, color: Theme.of(context).textTheme.bodyText2?.color,
), fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center, textAlign: TextAlign.center,
); );
} }
@ -219,9 +263,9 @@ class DisplayTrainOperator extends StatelessWidget {
return Text( return Text(
trainData.operator, trainData.operator,
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
fontSize: isSmallScreen(context) ? 12 : 14, fontSize: isSmallScreen(context) ? 12 : 14,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
); );
} }
@ -243,8 +287,8 @@ class DisplayTrainRoute extends StatelessWidget {
child: Text( child: Text(
trainData.route.from, trainData.route.from,
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: 16, fontSize: 16,
), ),
), ),
), ),
), ),
@ -257,8 +301,8 @@ class DisplayTrainRoute extends StatelessWidget {
child: Text( child: Text(
trainData.route.to, trainData.route.to,
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: 16, fontSize: 16,
), ),
textAlign: TextAlign.right, textAlign: TextAlign.right,
), ),
), ),
@ -282,10 +326,10 @@ class DisplayTrainDeparture extends StatelessWidget {
// "Plecare în ${dataPlecare.day.toString().padLeft(2, '0')}.${dataPlecare.month.toString().padLeft(2, '0')}.${dataPlecare.year.toString().padLeft(4, '0')}", // "Plecare în ${dataPlecare.day.toString().padLeft(2, '0')}.${dataPlecare.month.toString().padLeft(2, '0')}.${dataPlecare.year.toString().padLeft(4, '0')}",
"Plecare în ${trainData.date}", "Plecare în ${trainData.date}",
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontStyle: FontStyle.italic, fontStyle: FontStyle.italic,
fontWeight: FontWeight.w200, fontWeight: FontWeight.w200,
fontSize: isSmallScreen(context) ? 14 : 16, fontSize: isSmallScreen(context) ? 14 : 16,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
); );
@ -315,9 +359,9 @@ class DisplayTrainLastInfo extends StatelessWidget {
child: Text( child: Text(
"Ultima informație", "Ultima informație",
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 20 : 22, fontSize: isSmallScreen(context) ? 20 : 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
), ),
), ),
@ -328,19 +372,21 @@ class DisplayTrainLastInfo extends StatelessWidget {
child: Text( child: Text(
trainData.status!.station, trainData.status!.station,
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 16 : 18, fontSize: isSmallScreen(context) ? 16 : 18,
), ),
textAlign: TextAlign.left, textAlign: TextAlign.left,
), ),
), ),
Expanded(child: Container(),), Expanded(
child: Container(),
),
Padding( Padding(
padding: const EdgeInsets.all(4), padding: const EdgeInsets.all(4),
child: Text( child: Text(
stateToString(trainData.status!.state), stateToString(trainData.status!.state),
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 16 : 18, fontSize: isSmallScreen(context) ? 16 : 18,
), ),
textAlign: TextAlign.right, textAlign: TextAlign.right,
), ),
), ),
@ -359,7 +405,9 @@ class DisplayTrainLastInfo extends StatelessWidget {
// ); // );
// }, // },
// ), // ),
Expanded(child: Container(),), Expanded(
child: Container(),
),
Builder( Builder(
builder: (context) { builder: (context) {
final data = trainData.status!.delay; final data = trainData.status!.delay;
@ -370,19 +418,20 @@ class DisplayTrainLastInfo extends StatelessWidget {
if (data > 0) { if (data > 0) {
return Text( return Text(
"$data ${data == 1 ? 'minut' : 'minute'} întârziere", "$data ${data == 1 ? 'minut' : 'minute'} întârziere",
style: Theme.of(context).textTheme.bodyText2?.copyWith( style:
fontSize: isSmallScreen(context) ? 14 : 16, Theme.of(context).textTheme.bodyText2?.copyWith(
color: Colors.red.shade300, fontSize: isSmallScreen(context) ? 14 : 16,
), color: Colors.red.shade300,
),
); );
} } else {
else {
return Text( return Text(
"${-data} ${data == -1 ? 'minut' : 'minute'} mai devreme", "${-data} ${data == -1 ? 'minut' : 'minute'} mai devreme",
style: Theme.of(context).textTheme.bodyText2?.copyWith( style:
fontSize: isSmallScreen(context) ? 14 : 16, Theme.of(context).textTheme.bodyText2?.copyWith(
color: Colors.green.shade300, fontSize: isSmallScreen(context) ? 14 : 16,
), color: Colors.green.shade300,
),
); );
} }
}, },
@ -500,9 +549,9 @@ class DisplayTrainDestination extends StatelessWidget {
child: Text( child: Text(
"Destinația", "Destinația",
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 20 : 22, fontSize: isSmallScreen(context) ? 20 : 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
@ -511,18 +560,21 @@ class DisplayTrainDestination extends StatelessWidget {
child: Text( child: Text(
destination.name, destination.name,
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 18 : 20, fontSize: isSmallScreen(context) ? 18 : 20,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
), ),
Builder( Builder(
builder: (context) { builder: (context) {
final arrival = destination.arrival!.scheduleTime.toLocal(); final arrival = destination.arrival!.scheduleTime.toLocal();
final delay = trainData.stations.last.arrival!.status?.delay ?? 0; final delay =
final arrivalWithDelay = arrival.add(Duration(minutes: delay)); trainData.stations.last.arrival!.status?.delay ?? 0;
final arrivalWithDelayString = '${arrivalWithDelay.hour}:${arrivalWithDelay.minute.toString().padLeft(2, "0")}'; final arrivalWithDelay =
arrival.add(Duration(minutes: delay));
final arrivalWithDelayString =
'${arrivalWithDelay.hour}:${arrivalWithDelay.minute.toString().padLeft(2, "0")}';
// const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"]; // const months = ["ian", "feb", "mar", "apr", "mai", "iun", "iul", "aug", "sep", "oct", "noi", "dec"];
return Column( return Column(
@ -542,25 +594,30 @@ class DisplayTrainDestination extends StatelessWidget {
children: [ children: [
TextSpan(text: ' '), TextSpan(text: ' '),
TextSpan( TextSpan(
text: '${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}', text:
style: delay == 0 ? null : TextStyle( '${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}',
decoration: TextDecoration.lineThrough, style: delay == 0
), ? null
: TextStyle(
decoration: TextDecoration.lineThrough,
),
), ),
if (delay != 0) ...[ if (delay != 0) ...[
TextSpan(text: ' '), TextSpan(text: ' '),
TextSpan( TextSpan(
text: '$arrivalWithDelayString', text: '$arrivalWithDelayString',
style: TextStyle( style: TextStyle(
color: delay > 0 ? Colors.red.shade300 : Colors.green.shade300, color: delay > 0
? Colors.red.shade300
: Colors.green.shade300,
), ),
), ),
] ]
], ],
), ),
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 14 : 16, fontSize: isSmallScreen(context) ? 14 : 16,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
@ -592,16 +649,16 @@ class DisplayTrainRouteDistance extends StatelessWidget {
Text( Text(
"Distanța rutei", "Distanța rutei",
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 20 : 22, fontSize: isSmallScreen(context) ? 20 : 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
Text( Text(
"${trainData.stations.last.km} km", "${trainData.stations.last.km} km",
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 18 : 20, fontSize: isSmallScreen(context) ? 18 : 20,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
], ],
@ -629,22 +686,26 @@ class DisplayTrainRouteDuration extends StatelessWidget {
Text( Text(
"Durata rutei", "Durata rutei",
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 20 : 22, fontSize: isSmallScreen(context) ? 20 : 22,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
Builder( Builder(
builder: (context) { builder: (context) {
var duration = trainData.stations.last.arrival!.scheduleTime.difference(trainData.stations.first.departure!.scheduleTime); var duration = trainData.stations.last.arrival!.scheduleTime
.difference(
trainData.stations.first.departure!.scheduleTime);
var durationString = StringBuffer(); var durationString = StringBuffer();
bool firstWritten = false; bool firstWritten = false;
if (duration.inDays > 0) { if (duration.inDays > 0) {
firstWritten = true; firstWritten = true;
if (duration.inDays == 1) durationString.write("1 zi"); if (duration.inDays == 1)
else durationString.write("${duration.inDays} zile"); durationString.write("1 zi");
else
durationString.write("${duration.inDays} zile");
duration -= Duration(days: duration.inDays); duration -= Duration(days: duration.inDays);
} }
@ -653,8 +714,10 @@ class DisplayTrainRouteDuration extends StatelessWidget {
durationString.write(", "); durationString.write(", ");
} }
firstWritten = true; firstWritten = true;
if (duration.inHours == 1) durationString.write("1 oră"); if (duration.inHours == 1)
else durationString.write("${duration.inHours} ore"); durationString.write("1 oră");
else
durationString.write("${duration.inHours} ore");
duration -= Duration(hours: duration.inHours); duration -= Duration(hours: duration.inHours);
} }
@ -663,16 +726,18 @@ class DisplayTrainRouteDuration extends StatelessWidget {
durationString.write(", "); durationString.write(", ");
} }
firstWritten = true; firstWritten = true;
if (duration.inMinutes == 1) durationString.write("1 minut"); if (duration.inMinutes == 1)
else durationString.write("${duration.inMinutes} minute"); durationString.write("1 minut");
else
durationString.write("${duration.inMinutes} minute");
duration -= Duration(minutes: duration.inMinutes); duration -= Duration(minutes: duration.inMinutes);
} }
return Text( return Text(
durationString.toString(), durationString.toString(),
style: Theme.of(context).textTheme.bodyText2?.copyWith( style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 18 : 20, fontSize: isSmallScreen(context) ? 18 : 20,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
); );
}, },
@ -685,8 +750,10 @@ class DisplayTrainRouteDuration extends StatelessWidget {
} }
} }
class DisplayTrainYesterdayWarningMaterial extends DisplayTrainYesterdayWarningCommon { class DisplayTrainYesterdayWarningMaterial
DisplayTrainYesterdayWarningMaterial(void Function() onViewYesterdayTrain) : super(onViewYesterdayTrain); extends DisplayTrainYesterdayWarningCommon {
DisplayTrainYesterdayWarningMaterial(void Function() onViewYesterdayTrain)
: super(onViewYesterdayTrain);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -698,7 +765,9 @@ class DisplayTrainYesterdayWarningMaterial extends DisplayTrainYesterdayWarningC
child: Text.rich( child: Text.rich(
TextSpan( TextSpan(
children: [ children: [
TextSpan(text: DisplayTrainYesterdayWarningCommon.trainDidNotDepart,), TextSpan(
text: DisplayTrainYesterdayWarningCommon.trainDidNotDepart,
),
TextSpan(text: '\n'), TextSpan(text: '\n'),
TextSpan( TextSpan(
text: DisplayTrainYesterdayWarningCommon.seeYesterdayTrain, text: DisplayTrainYesterdayWarningCommon.seeYesterdayTrain,
@ -730,6 +799,12 @@ class DisplayTrainStations extends StatelessWidget {
return IndexedSemantics( return IndexedSemantics(
child: DisplayTrainStation( child: DisplayTrainStation(
station: trainData.stations[index], station: trainData.stations[index],
onTap: () {
Navigator.of(context).pushNamed(
ViewStationPage.routeName,
arguments: trainData.stations[index].name,
);
},
), ),
index: index, index: index,
); );
@ -740,4 +815,3 @@ class DisplayTrainStations extends StatelessWidget {
); );
} }
} }

215
lib/pages/train_info_page/view_train/train_info_material_DisplayTrainStation.dart

@ -1,163 +1,92 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:info_tren/models/train_data.dart'; import 'package:info_tren/models/train_data.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart' show isSmallScreen; import 'package:info_tren/components/badge.dart';
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
class DisplayTrainStation extends StatelessWidget { class DisplayTrainStation extends StatelessWidget {
final Station station; final Station station;
final void Function()? onTap;
DisplayTrainStation({required this.station}); DisplayTrainStation({required this.station, this.onTap});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Card( return Card(
child: Padding( child: InkWell(
padding: const EdgeInsets.all(2), onTap: onTap,
child: Column( child: Padding(
mainAxisSize: MainAxisSize.min, padding: const EdgeInsets.all(2),
crossAxisAlignment: CrossAxisAlignment.center, child: Column(
children: <Widget>[ mainAxisSize: MainAxisSize.min,
Row( crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max, children: <Widget>[
children: <Widget>[ Row(
Expanded( mainAxisSize: MainAxisSize.max,
flex: 1, children: <Widget>[
child: Align( Expanded(
alignment: Alignment.centerLeft, flex: 1,
child: Builder( child: Align(
builder: (context) { alignment: Alignment.centerLeft,
final departureStatus = station.departure?.status; child: Builder(
final arrivalStatus = station.arrival?.status; builder: (context) {
int delay; final departureStatus = station.departure?.status;
bool real; final arrivalStatus = station.arrival?.status;
if (departureStatus == null) { int delay;
delay = arrivalStatus?.delay ?? 0; bool real;
real = arrivalStatus?.real ?? false; if (departureStatus == null) {
} delay = arrivalStatus?.delay ?? 0;
else if (arrivalStatus == null) { real = arrivalStatus?.real ?? false;
delay = departureStatus.delay; }
real = departureStatus.real; else if (arrivalStatus == null) {
} delay = departureStatus.delay;
else { real = departureStatus.real;
delay = departureStatus.delay; }
real = departureStatus.real; else {
if (!real && arrivalStatus.real) { delay = departureStatus.delay;
delay = arrivalStatus.delay; real = departureStatus.real;
real = arrivalStatus.real; if (!real && arrivalStatus.real) {
delay = arrivalStatus.delay;
real = arrivalStatus.real;
}
} }
}
final isDelayed = delay > 0 && real == true; final isDelayed = delay > 0 && real == true;
final isOnTime = delay <= 0 && real == true; final isOnTime = delay <= 0 && real == true;
final isNotScheduled = false; final isNotScheduled = false;
return Badge( return MaterialBadge(
text: station.km.toString(), text: station.km.toString(),
caption: 'km', caption: 'km',
isNotScheduled: isNotScheduled, isNotScheduled: isNotScheduled,
isDelayed: isDelayed, isDelayed: isDelayed,
isOnTime: isOnTime, isOnTime: isOnTime,
); );
} }
),
), ),
), ),
), Title(
Title( station: station,
station: station, ),
), Expanded(
Expanded( flex: 1,
flex: 1, child: (station.platform == null)
child: (station.platform == null) ? Container()
? Container() : Align(
: Align( alignment: Alignment.centerRight,
alignment: Alignment.centerRight, child: MaterialBadge(text: station.platform!, caption: 'linia',),
child: Badge(text: station.platform!, caption: 'linia',), ),
),
),
],
),
Time(
station: station,
),
Delay(
station: station,
),
],
),
),
);
}
}
class Badge extends StatelessWidget {
final String text;
final String caption;
final bool isNotScheduled;
final bool isOnTime;
final bool isDelayed;
Badge({
required this.text,
required this.caption,
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: <Widget>[
Expanded(
child: Center(
child: Text(
text,
style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: isSmallScreen(context) ? 16 : 20,
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor,
), ),
textAlign: TextAlign.center, ],
),
), ),
), Time(
Text( station: station,
caption,
style: Theme.of(context).textTheme.bodyText2?.copyWith(
fontSize: 10,
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor,
), ),
), Delay(
], station: station,
),
],
),
), ),
), ),
); );

2
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. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 2.7.4 version: 2.7.6
environment: environment:
sdk: ">=2.15.0 <3.0.0" sdk: ">=2.15.0 <3.0.0"

Loading…
Cancel
Save