Add Fluent UI for Win, Linux
Also add split screen train view for landscape
This commit is contained in:
parent
da983871e2
commit
0e484bdc16
30 changed files with 3062 additions and 612 deletions
|
@ -1,3 +1,9 @@
|
|||
v2.7.9
|
||||
Add Fluent UI for Windows and Linux.
|
||||
Add split view in landscape when viewing a train.
|
||||
Add error handling and auto refresh when viewing a station's arrivals/departures.
|
||||
General code fixes and migration (freezed, riverpod).
|
||||
|
||||
v2.7.8
|
||||
Added cancelled trains in departures/arrivals board.
|
||||
Selecting train in departures/arrivels board chooses appropriate departure date.
|
||||
|
|
59
lib/components/badge/badge.dart
Normal file
59
lib/components/badge/badge.dart
Normal file
|
@ -0,0 +1,59 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/components/badge/badge_cupertino.dart';
|
||||
import 'package:info_tren/components/badge/badge_fluent.dart';
|
||||
import 'package:info_tren/components/badge/badge_material.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
||||
class Badge extends ConsumerWidget {
|
||||
final String text;
|
||||
final String caption;
|
||||
final bool isNotScheduled;
|
||||
final bool isOnTime;
|
||||
final bool isDelayed;
|
||||
|
||||
const Badge({
|
||||
super.key,
|
||||
required this.text,
|
||||
required this.caption,
|
||||
this.isNotScheduled = false,
|
||||
this.isOnTime = false,
|
||||
this.isDelayed = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final uiDesign = ref.watch(uiDesignProvider);
|
||||
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return MaterialBadge(
|
||||
text: text,
|
||||
caption: caption,
|
||||
isNotScheduled: isNotScheduled,
|
||||
isOnTime: isOnTime,
|
||||
isDelayed: isDelayed,
|
||||
);
|
||||
case UiDesign.CUPERTINO:
|
||||
return CupertinoBadge(
|
||||
text: text,
|
||||
caption: caption,
|
||||
isNotScheduled: isNotScheduled,
|
||||
isOnTime: isOnTime,
|
||||
isDelayed: isDelayed,
|
||||
);
|
||||
case UiDesign.FLUENT:
|
||||
return FluentBadge(
|
||||
text: text,
|
||||
caption: caption,
|
||||
isNotScheduled: isNotScheduled,
|
||||
isOnTime: isOnTime,
|
||||
isDelayed: isDelayed,
|
||||
);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,84 +1,5 @@
|
|||
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;
|
||||
|
||||
const MaterialBadge({
|
||||
required this.text,
|
||||
required this.caption,
|
||||
this.isNotScheduled = false,
|
||||
this.isOnTime = false,
|
||||
this.isDelayed = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@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.bodyMedium?.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.bodyMedium?.copyWith(
|
||||
fontSize: 10,
|
||||
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CupertinoBadge extends StatelessWidget {
|
||||
final String text;
|
80
lib/components/badge/badge_fluent.dart
Normal file
80
lib/components/badge/badge_fluent.dart
Normal file
|
@ -0,0 +1,80 @@
|
|||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
|
||||
|
||||
class FluentBadge extends StatelessWidget {
|
||||
final String text;
|
||||
final String caption;
|
||||
final bool isNotScheduled;
|
||||
final bool isOnTime;
|
||||
final bool isDelayed;
|
||||
|
||||
const FluentBadge({
|
||||
required this.text,
|
||||
required this.caption,
|
||||
this.isNotScheduled = false,
|
||||
this.isOnTime = false,
|
||||
this.isDelayed = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Color foregroundColor = FluentTheme.of(context).activeColor;
|
||||
Color? backgroundColor;
|
||||
|
||||
if (isNotScheduled) {
|
||||
foregroundColor = const Color.fromRGBO(225, 175, 30, 1);
|
||||
backgroundColor = const Color.fromRGBO(80, 40, 10, 1);
|
||||
}
|
||||
else if (isOnTime) {
|
||||
foregroundColor = const Color.fromRGBO(130, 175, 65, 1);
|
||||
backgroundColor = const Color.fromRGBO(40, 80, 10, 1);
|
||||
}
|
||||
else if (isDelayed) {
|
||||
foregroundColor = const Color.fromRGBO(225, 75, 30, 1);
|
||||
backgroundColor = const 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: FluentTheme.of(context).typography.bodyLarge?.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w400 : FontWeight.w200,
|
||||
color: MediaQuery.of(context).boldText ? Colors.white : foregroundColor,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
caption,
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 12,
|
||||
color: MediaQuery.of(context).boldText ? Colors.white : foregroundColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
79
lib/components/badge/badge_material.dart
Normal file
79
lib/components/badge/badge_material.dart
Normal file
|
@ -0,0 +1,79 @@
|
|||
import 'package:flutter/material.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;
|
||||
|
||||
const MaterialBadge({
|
||||
required this.text,
|
||||
required this.caption,
|
||||
this.isNotScheduled = false,
|
||||
this.isOnTime = false,
|
||||
this.isDelayed = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@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.bodyMedium?.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.bodyMedium?.copyWith(
|
||||
fontSize: 10,
|
||||
color: MediaQuery.of(context).boldText ? Colors.white70 : foregroundColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/components/loading/loading_cupertino.dart';
|
||||
import 'package:info_tren/components/loading/loading_fluent.dart';
|
||||
import 'package:info_tren/components/loading/loading_material.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
@ -19,6 +20,8 @@ class Loading extends ConsumerWidget {
|
|||
return LoadingMaterial(text: text ?? defaultText,);
|
||||
case UiDesign.CUPERTINO:
|
||||
return LoadingCupertino(text: text ?? defaultText,);
|
||||
case UiDesign.FLUENT:
|
||||
return LoadingFluent(text: text ?? defaultText,);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
|
|
26
lib/components/loading/loading_fluent.dart
Normal file
26
lib/components/loading/loading_fluent.dart
Normal file
|
@ -0,0 +1,26 @@
|
|||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:info_tren/components/loading/loading.dart';
|
||||
|
||||
class LoadingFluent extends LoadingCommon {
|
||||
const LoadingFluent({required super.text, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Center(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: ProgressRing(),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(text),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_cupertino.dart';
|
||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_fluent.dart';
|
||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions_material.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
@ -31,6 +32,12 @@ class SelectTrainSuggestions extends ConsumerWidget {
|
|||
onTrainSelected: onTrainSelected,
|
||||
currentInput: currentInput,
|
||||
);
|
||||
case UiDesign.FLUENT:
|
||||
return SelectTrainSuggestionsFluent(
|
||||
choices: choices,
|
||||
onTrainSelected: onTrainSelected,
|
||||
currentInput: currentInput,
|
||||
);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
|
@ -102,6 +109,12 @@ class OperatorAutocompleteSliver extends ConsumerWidget {
|
|||
operatorName: operatorName,
|
||||
train: train,
|
||||
);
|
||||
case UiDesign.FLUENT:
|
||||
return OperatorAutocompleteTileFluent(
|
||||
onTrainSelected: onTrainSelected,
|
||||
operatorName: operatorName,
|
||||
train: train,
|
||||
);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
|
||||
class SelectTrainSuggestionsFluent extends SelectTrainSuggestionsShared {
|
||||
const SelectTrainSuggestionsFluent({
|
||||
super.key,
|
||||
required super.choices,
|
||||
required super.onTrainSelected,
|
||||
super.currentInput,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget getUseCurrentInputWidget(String currentInput, void Function(String) onTrainSelected) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Button(
|
||||
child: Text(getUseCurrentInputWidgetText(currentInput)),
|
||||
onPressed: () => onTrainSelected(currentInput),
|
||||
),
|
||||
],
|
||||
)
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class OperatorAutocompleteTileFluent extends OperatorAutocompleteTile {
|
||||
const OperatorAutocompleteTileFluent({
|
||||
Key? key,
|
||||
required String operatorName,
|
||||
required void Function(String) onTrainSelected,
|
||||
required TrainsResult train
|
||||
}): super(
|
||||
onTrainSelected: onTrainSelected,
|
||||
operatorName: operatorName,
|
||||
train: train,
|
||||
key: key,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
onTrainSelected(train.number);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 4, 16, 4),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
operatorName,
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(fontSize: 10, fontWeight: FontWeight.w200),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
Text(
|
||||
"${train.rank} ${train.number}",
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart' as f;
|
||||
import 'package:flutter/cupertino.dart' as c;
|
||||
import 'package:flutter/material.dart' as m;
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/about/about_page.dart';
|
||||
import 'package:info_tren/pages/main/main_page.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
|
||||
|
@ -54,48 +59,59 @@ Map<String, WidgetBuilder> get routes => {
|
|||
},
|
||||
};
|
||||
|
||||
class StartPoint extends StatelessWidget {
|
||||
class StartPoint extends ConsumerWidget {
|
||||
final String appTitle = 'Info Tren';
|
||||
|
||||
const StartPoint({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// if (Platform.isIOS || Platform.isMacOS) {
|
||||
// return AnnotatedRegion(
|
||||
// value: const SystemUiOverlayStyle(
|
||||
// statusBarBrightness: Brightness.dark,
|
||||
// ),
|
||||
// child: CupertinoApp(
|
||||
// title: appTitle,
|
||||
// theme: CupertinoThemeData(
|
||||
// primaryColor: Colors.blue.shade600,
|
||||
// brightness: Brightness.dark,
|
||||
// // textTheme: CupertinoTextThemeData(
|
||||
// // textStyle: TextStyle(
|
||||
// // fontFamily: 'Atkinson Hyperlegible',
|
||||
// // ),
|
||||
// // ),
|
||||
// ),
|
||||
// routes: routesByUiDesign(UiDesign.CUPERTINO),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// else {
|
||||
return MaterialApp(
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final uiDesign = ref.watch(uiDesignProvider);
|
||||
if (uiDesign == UiDesign.CUPERTINO) {
|
||||
return AnnotatedRegion(
|
||||
value: const SystemUiOverlayStyle(
|
||||
statusBarBrightness: c.Brightness.dark,
|
||||
),
|
||||
child: c.CupertinoApp(
|
||||
title: appTitle,
|
||||
theme: ThemeData(
|
||||
primarySwatch: Colors.blue,
|
||||
colorScheme: ColorScheme.fromSwatch(
|
||||
brightness: Brightness.dark,
|
||||
primarySwatch: Colors.blue,
|
||||
accentColor: Colors.blue.shade700,
|
||||
theme: c.CupertinoThemeData(
|
||||
primaryColor: m.Colors.blue.shade600,
|
||||
brightness: c.Brightness.dark,
|
||||
// textTheme: CupertinoTextThemeData(
|
||||
// textStyle: TextStyle(
|
||||
// fontFamily: 'Atkinson Hyperlegible',
|
||||
// ),
|
||||
// ),
|
||||
),
|
||||
routes: routes,
|
||||
),
|
||||
);
|
||||
}
|
||||
else if (uiDesign == UiDesign.FLUENT) {
|
||||
return f.FluentApp(
|
||||
title: appTitle,
|
||||
theme: f.ThemeData(
|
||||
brightness: f.Brightness.dark,
|
||||
accentColor: f.Colors.blue,
|
||||
),
|
||||
routes: routes,
|
||||
);
|
||||
}
|
||||
else {
|
||||
return m.MaterialApp(
|
||||
title: appTitle,
|
||||
theme: m.ThemeData(
|
||||
primarySwatch: m.Colors.blue,
|
||||
colorScheme: m.ColorScheme.fromSwatch(
|
||||
brightness: m.Brightness.dark,
|
||||
primarySwatch: m.Colors.blue,
|
||||
accentColor: m.Colors.blue.shade700,
|
||||
),
|
||||
useMaterial3: true,
|
||||
// fontFamily: 'Atkinson Hyperlegible',
|
||||
),
|
||||
routes: routes,
|
||||
);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
enum UiDesign {
|
||||
MATERIAL,
|
||||
CUPERTINO
|
||||
CUPERTINO,
|
||||
FLUENT,
|
||||
}
|
||||
|
||||
class UnmatchedUiDesignException implements Exception {
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/about/about_page.dart';
|
||||
import 'package:info_tren/pages/main/main_page_cupertino.dart';
|
||||
import 'package:info_tren/pages/main/main_page_fluent.dart';
|
||||
import 'package:info_tren/pages/main/main_page_material.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
|
||||
|
@ -20,6 +21,8 @@ class MainPage extends ConsumerWidget {
|
|||
return const MainPageMaterial();
|
||||
case UiDesign.CUPERTINO:
|
||||
return const MainPageCupertino();
|
||||
case UiDesign.FLUENT:
|
||||
return const MainPageFluent();
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
|
|
73
lib/pages/main/main_page_fluent.dart
Normal file
73
lib/pages/main/main_page_fluent.dart
Normal file
|
@ -0,0 +1,73 @@
|
|||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:info_tren/pages/main/main_page.dart';
|
||||
|
||||
class MainPageFluent extends MainPageShared {
|
||||
const MainPageFluent({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return NavigationView(
|
||||
appBar: NavigationAppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
title: Text(pageTitle),
|
||||
// centerTitle: true,
|
||||
// actions: [
|
||||
// PopupMenuButton<int>(
|
||||
// icon: const Icon(Icons.more_vert),
|
||||
// tooltip: moreOptionsText,
|
||||
// itemBuilder: (_) => popupMenu.asMap().entries.map((e) => PopupMenuItem(
|
||||
// value: e.key,
|
||||
// child: Text(e.value.name),
|
||||
// )).toList(),
|
||||
// onSelected: (index) {
|
||||
// popupMenu[index].action?.call(context);
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
),
|
||||
content: SafeArea(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: options.map((option) => HoverButton(
|
||||
onPressed: option.action != null ? () => option.action!(context) : null,
|
||||
builder: (context, states) {
|
||||
return Card(
|
||||
backgroundColor: option.action != null ? (states.isHovering ? FluentTheme.of(context).accentColor.dark : FluentTheme.of(context).accentColor) : null,
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
option.name,
|
||||
style: FluentTheme.of(context)
|
||||
.typography
|
||||
.bodyLarge
|
||||
?.copyWith(
|
||||
color:
|
||||
FluentTheme.of(context).activeColor,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(option.description!),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
)).map((w) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 2, 4, 2),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: w,
|
||||
),
|
||||
)).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import 'package:flutter/widgets.dart';
|
|||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station_cupertino.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station_fluent.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station_material.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
@ -20,6 +21,8 @@ class SelectStationPage extends ConsumerWidget {
|
|||
return const SelectStationPageMaterial();
|
||||
case UiDesign.CUPERTINO:
|
||||
return const SelectStationPageCupertino();
|
||||
case UiDesign.FLUENT:
|
||||
return const SelectStationPageFluent();
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart';
|
||||
|
||||
class SelectStationPageFluent extends SelectStationPageShared {
|
||||
const SelectStationPageFluent({super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => SelectStationPageStateFluent();
|
||||
}
|
||||
|
||||
class SelectStationPageStateFluent extends SelectStationPageState {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return NavigationView(
|
||||
appBar: const NavigationAppBar(
|
||||
title: Text(SelectStationPageState.pageTitle),
|
||||
// centerTitle: true,
|
||||
),
|
||||
content: SafeArea(
|
||||
bottom: false,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: TextBox(
|
||||
controller: textEditingController,
|
||||
autofocus: true,
|
||||
placeholder: SelectStationPageState.textFieldLabel,
|
||||
textInputAction: TextInputAction.search,
|
||||
onChanged: onTextChanged,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
// dense: true,
|
||||
title: Text(filteredStations[index]),
|
||||
onPressed: () => onSuggestionSelected(filteredStations[index]),
|
||||
),
|
||||
const Divider(
|
||||
size: 1,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
itemCount: filteredStations.length,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:info_tren/components/refresh_future_builder.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station_cupertino.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station_fluent.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station_material.dart';
|
||||
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
@ -31,6 +32,11 @@ class ViewStationPage extends HookConsumerWidget {
|
|||
tab: tab.value,
|
||||
setTab: (newTab) => tab.value = newTab,
|
||||
);
|
||||
case UiDesign.FLUENT:
|
||||
return ViewStationPageFluent(
|
||||
tab: tab.value,
|
||||
setTab: (newTab) => tab.value = newTab,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,267 @@
|
|||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/components/badge/badge.dart';
|
||||
import 'package:info_tren/components/loading/loading.dart';
|
||||
import 'package:info_tren/components/refresh_future_builder.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
||||
class ViewStationPageFluent extends ViewStationPageShared {
|
||||
const ViewStationPageFluent({super.key, required super.tab, required super.setTab});
|
||||
|
||||
@override
|
||||
Widget buildContent(BuildContext context, Future Function() refresh, Future Function(Future<StationData> Function()) _, RefreshFutureBuilderSnapshot<StationData> snapshot) {
|
||||
return NavigationView(
|
||||
appBar: NavigationAppBar(
|
||||
title: Consumer(
|
||||
builder: (context, ref, _) {
|
||||
final stationName = ref.watch(viewStationArgumentsProvider.select((value) => value.stationName));
|
||||
return Text(snapshot.hasData ? snapshot.data!.stationName : stationName);
|
||||
}
|
||||
),
|
||||
),
|
||||
content: snapshot.state == RefreshFutureBuilderState.waiting || snapshot.state == RefreshFutureBuilderState.refreshError
|
||||
? const Loading(text: ViewStationPageShared.loadingText,)
|
||||
: snapshot.state == RefreshFutureBuilderState.error
|
||||
? Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(
|
||||
FluentIcons.error,
|
||||
size: 32,
|
||||
color: Colors.red.normal,
|
||||
),
|
||||
const Text(
|
||||
ViewStationPageShared.errorText,
|
||||
style: TextStyle(
|
||||
inherit: true,
|
||||
fontSize: 32,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
snapshot.error.toString(),
|
||||
style: FluentTheme.of(context).typography.subtitle,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Button(
|
||||
onPressed: () {
|
||||
refresh();
|
||||
},
|
||||
child: const Text(ViewStationPageShared.retryText),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
pane: snapshot.hasData ? NavigationPane(
|
||||
onChanged: onTabChange,
|
||||
selected: tab.index,
|
||||
items: [
|
||||
PaneItem(
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: SafeArea(left: false, bottom: false, right: false,child: Container(),),),
|
||||
SliverList(
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
title: const Text(ViewStationPageShared.arrivals),
|
||||
icon: const Icon(FluentIcons.arrow_down_right8),
|
||||
),
|
||||
PaneItem(
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: SafeArea(left: false, bottom: false, right: false,child: Container(),),),
|
||||
SliverList(
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
title: const Text(ViewStationPageShared.departures),
|
||||
icon: const Icon(FluentIcons.arrow_up_right8),
|
||||
),
|
||||
],
|
||||
) : null,
|
||||
// bottomNavigationBar: snapshot.hasData ? BottomNavigationBar(
|
||||
// items: const [
|
||||
// BottomNavigationBarItem(
|
||||
// icon: Icon(Icons.arrow_downward),
|
||||
// label: ViewStationPageShared.arrivals,
|
||||
// ),
|
||||
// BottomNavigationBarItem(
|
||||
// icon: Icon(Icons.arrow_upward),
|
||||
// label: ViewStationPageShared.departures,
|
||||
// ),
|
||||
// ],
|
||||
// currentIndex: tab.index,
|
||||
// onTap: onTabChange,
|
||||
// ) : null,
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildStationItem(BuildContext context, StationArrDep item, {required bool arrival}) {
|
||||
return HoverButton(
|
||||
onPressed: () => onTrainTapped(context, item.train),
|
||||
builder: (context, states) {
|
||||
return Container(
|
||||
color: item.status.cancelled
|
||||
? Colors.red.withAlpha(100)
|
||||
: states.isPressing
|
||||
? FluentTheme.of(context).scaffoldBackgroundColor
|
||||
: states.isHovering
|
||||
? FluentTheme.of(context).inactiveBackgroundColor
|
||||
: null,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}',
|
||||
style: TextStyle(
|
||||
inherit: true,
|
||||
fontFeatures: const [
|
||||
FontFeature.tabularFigures(),
|
||||
],
|
||||
decoration: item.status.cancelled || item.status.delay != 0 ? TextDecoration.lineThrough : null,
|
||||
fontSize: item.status.delay != 0 ? 12 : null,
|
||||
),
|
||||
),
|
||||
if (item.status.delay != 0) Builder(
|
||||
builder: (context) {
|
||||
final newTime = item.time.add(Duration(minutes: item.status.delay));
|
||||
final delay = item.status.delay > 0;
|
||||
|
||||
return Text(
|
||||
'${newTime.toLocal().hour.toString().padLeft(2, '0')}:${newTime.toLocal().minute.toString().padLeft(2, '0')}',
|
||||
style: TextStyle(
|
||||
inherit: true,
|
||||
fontFeatures: const [
|
||||
FontFeature.tabularFigures(),
|
||||
],
|
||||
color: delay ? Colors.red : Colors.green,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: IgnorePointer(
|
||||
child: ListTile(
|
||||
// isThreeLine: item.status.delay != 0,
|
||||
title: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: item.train.rank,
|
||||
style: TextStyle(
|
||||
color: item.train.rank.startsWith('IR') ? const Color.fromARGB(255, 255, 0, 0) : null,
|
||||
),
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(text: item.train.number,),
|
||||
],
|
||||
),
|
||||
),
|
||||
subtitle: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: item.status.cancelled
|
||||
? (arrival ? ViewStationPageShared.cancelledArrival : ViewStationPageShared.cancelledDeparture)
|
||||
: item.time.add(Duration(minutes: max(0, item.status.delay))).compareTo(DateTime.now()) < 0
|
||||
? (arrival ? ViewStationPageShared.arrivedFrom : ViewStationPageShared.departedTo)
|
||||
: (arrival ? ViewStationPageShared.arrivesFrom : ViewStationPageShared.departsTo)
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(text: item.train.terminus),
|
||||
if (item.status.delay != 0) ...[
|
||||
const TextSpan(text: '\n'),
|
||||
if (item.status.delay.abs() >= 60) ...[
|
||||
TextSpan(text: (item.status.delay.abs() ~/ 60).toString()),
|
||||
TextSpan(text: item.status.delay.abs() >= 120 ? ' ore' : ' oră'),
|
||||
if (item.status.delay.abs() % 60 != 0)
|
||||
const TextSpan(text: ' și '),
|
||||
],
|
||||
TextSpan(text: (item.status.delay.abs() % 60).toString()),
|
||||
TextSpan(text: item.status.delay.abs() > 1 ? ' minute' : ' minut'),
|
||||
const TextSpan(text: ' '),
|
||||
if (item.status.delay > 0)
|
||||
TextSpan(
|
||||
text: 'întârziere',
|
||||
style: TextStyle(
|
||||
inherit: true,
|
||||
color: Colors.red,
|
||||
),
|
||||
)
|
||||
else
|
||||
TextSpan(
|
||||
text: 'mai devreme',
|
||||
style: TextStyle(
|
||||
inherit: true,
|
||||
color: Colors.green,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (item.status.platform != null)
|
||||
IntrinsicHeight(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Badge(
|
||||
text: item.status.platform!,
|
||||
caption: 'Linia',
|
||||
isOnTime: item.status.real && item.status.delay <= 0,
|
||||
isDelayed: item.status.real && item.status.delay > 0,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildStationArrivalItem(BuildContext context, StationArrDep item) {
|
||||
return buildStationItem(context, item, arrival: true);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildStationDepartureItem(BuildContext context, StationArrDep item) {
|
||||
return buildStationItem(context, item, arrival: false);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import 'dart:math';
|
|||
import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/components/badge.dart';
|
||||
import 'package:info_tren/components/badge/badge.dart';
|
||||
import 'package:info_tren/components/loading/loading.dart';
|
||||
import 'package:info_tren/components/refresh_future_builder.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
|
@ -205,7 +205,7 @@ class ViewStationPageMaterial extends ViewStationPageShared {
|
|||
IntrinsicHeight(
|
||||
child: AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: MaterialBadge(
|
||||
child: Badge(
|
||||
text: item.status.platform!,
|
||||
caption: 'Linia',
|
||||
isOnTime: item.status.real && item.status.delay <= 0,
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||
import 'package:info_tren/components/select_train_suggestions/select_train_suggestions.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train_cupertino.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train_fluent.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train_material.dart';
|
||||
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
@ -26,6 +27,8 @@ class SelectTrainPage extends ConsumerWidget {
|
|||
return const SelectTrainPageMaterial();
|
||||
case UiDesign.CUPERTINO:
|
||||
return const SelectTrainPageCupertino();
|
||||
case UiDesign.FLUENT:
|
||||
return const SelectTrainPageFluent();
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
|
@ -70,7 +73,11 @@ abstract class SelectTrainPageState extends State<SelectTrainPageShared> {
|
|||
return posInNum1.compareTo(posInNum2);
|
||||
}
|
||||
|
||||
if (t1.number.length != t2.number.length) {
|
||||
return t1.number.length.compareTo(t2.number.length);
|
||||
}
|
||||
|
||||
return t1.number.compareTo(t2.number);
|
||||
});
|
||||
|
||||
return filtered;
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:info_tren/pages/train_info_page/select_train/select_train.dart';
|
||||
|
||||
class SelectTrainPageFluent extends SelectTrainPageShared {
|
||||
const SelectTrainPageFluent({super.key});
|
||||
|
||||
@override
|
||||
State<SelectTrainPageShared> createState() => SelectTrainPageStateFluent();
|
||||
}
|
||||
|
||||
class SelectTrainPageStateFluent extends SelectTrainPageState {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return NavigationView(
|
||||
appBar: NavigationAppBar(
|
||||
title: Text(pageTitle),
|
||||
),
|
||||
content: SafeArea(
|
||||
bottom: false,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: TextBox(
|
||||
controller: trainNoController,
|
||||
autofocus: true,
|
||||
placeholder: textFieldLabel,
|
||||
textInputAction: TextInputAction.search,
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (_) => onTextChanged(),
|
||||
inputFormatters: [
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: suggestionsList,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,12 +1,11 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:info_tren/api/train_data.dart';
|
||||
import 'package:info_tren/components/loading/loading.dart';
|
||||
import 'package:info_tren/components/refresh_future_builder.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/pages/train_info_page/view_train/train_info_cupertino.dart';
|
||||
import 'package:info_tren/pages/train_info_page/view_train/train_info_fluent.dart';
|
||||
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
|
||||
import 'package:info_tren/providers.dart';
|
||||
|
||||
|
@ -30,34 +29,35 @@ class TrainInfo extends ConsumerWidget {
|
|||
replaceFutureBuilder(() => getTrain(trainNumber, date: DateTime.now().subtract(const Duration(days: 1))));
|
||||
}
|
||||
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
if ([RefreshFutureBuilderState.none, RefreshFutureBuilderState.waiting].contains(snapshot.state)) {
|
||||
return TrainInfoLoadingMaterial(title: trainNumber.toString(), loadingText: "Se încarcă...",);
|
||||
return TrainInfoLoading(title: trainNumber.toString(), loadingText: "Se încarcă...",);
|
||||
}
|
||||
else if (snapshot.state == RefreshFutureBuilderState.error) {
|
||||
return TrainInfoErrorMaterial(title: '$trainNumber - Error', error: snapshot.error!, refresh: refresh,);
|
||||
return TrainInfoError(title: '$trainNumber - Error', error: snapshot.error!, refresh: refresh,);
|
||||
}
|
||||
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return TrainInfoMaterial(
|
||||
trainData: snapshot.data!,
|
||||
refresh: refresh,
|
||||
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
|
||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||
);
|
||||
case UiDesign.CUPERTINO:
|
||||
if ([RefreshFutureBuilderState.none, RefreshFutureBuilderState.waiting].contains(snapshot.state)) {
|
||||
return TrainInfoLoadingCupertino(title: trainNumber.toString(), loadingText: "Se încarcă...",);
|
||||
}
|
||||
else if (snapshot.state == RefreshFutureBuilderState.error) {
|
||||
return TrainInfoErrorCupertino(title: '$trainNumber - Error', error: snapshot.error!, refresh: refresh,);
|
||||
}
|
||||
|
||||
return TrainInfoCupertino(
|
||||
trainData: snapshot.data!,
|
||||
refresh: refresh,
|
||||
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
|
||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||
);
|
||||
case UiDesign.FLUENT:
|
||||
return TrainInfoFluent(
|
||||
trainData: snapshot.data!,
|
||||
refresh: refresh,
|
||||
isRefreshing: snapshot.state == RefreshFutureBuilderState.refreshing,
|
||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||
);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
|
@ -73,23 +73,176 @@ class TrainInfoArguments {
|
|||
TrainInfoArguments({required this.trainNumber, this.date});
|
||||
}
|
||||
|
||||
abstract class TrainInfoLoading extends StatelessWidget {
|
||||
abstract class TrainInfoShared extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
final Future Function()? refresh;
|
||||
final void Function()? onViewYesterdayTrain;
|
||||
final bool? isRefreshing;
|
||||
|
||||
const TrainInfoShared({
|
||||
super.key,
|
||||
required this.trainData,
|
||||
this.refresh,
|
||||
this.onViewYesterdayTrain,
|
||||
this.isRefreshing,
|
||||
});
|
||||
}
|
||||
|
||||
class TrainInfoLoading extends ConsumerWidget {
|
||||
final String title;
|
||||
final String? loadingText;
|
||||
|
||||
const TrainInfoLoading({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.loadingText,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final uiDesign = ref.watch(uiDesignProvider);
|
||||
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return TrainInfoLoadingMaterial(
|
||||
title: title,
|
||||
loadingText: loadingText,
|
||||
);
|
||||
case UiDesign.CUPERTINO:
|
||||
return TrainInfoLoadingCupertino(
|
||||
title: title,
|
||||
loadingText: loadingText,
|
||||
);
|
||||
case UiDesign.FLUENT:
|
||||
return TrainInfoLoadingFluent(
|
||||
title: title,
|
||||
loadingText: loadingText,
|
||||
);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TrainInfoLoadingShared extends StatelessWidget {
|
||||
final String title;
|
||||
final Widget loadingWidget;
|
||||
|
||||
TrainInfoLoading({
|
||||
TrainInfoLoadingShared({
|
||||
required this.title,
|
||||
String? loadingText,
|
||||
super.key,
|
||||
}) : loadingWidget = Loading(text: loadingText,);
|
||||
}
|
||||
|
||||
abstract class TrainInfoError extends StatelessWidget {
|
||||
class TrainInfoError extends ConsumerWidget {
|
||||
final String title;
|
||||
final Object error;
|
||||
final Future Function()? refresh;
|
||||
|
||||
const TrainInfoError({required this.title, required this.error, this.refresh, super.key,});
|
||||
const TrainInfoError({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.error,
|
||||
this.refresh,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final uiDesign = ref.watch(uiDesignProvider);
|
||||
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return TrainInfoErrorMaterial(
|
||||
title: title,
|
||||
error: error,
|
||||
refresh: refresh,
|
||||
);
|
||||
case UiDesign.CUPERTINO:
|
||||
return TrainInfoErrorCupertino(
|
||||
title: title,
|
||||
error: error,
|
||||
refresh: refresh,
|
||||
);
|
||||
case UiDesign.FLUENT:
|
||||
return TrainInfoErrorFluent(
|
||||
title: title,
|
||||
error: error,
|
||||
refresh: refresh,
|
||||
);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TrainInfoErrorShared extends StatelessWidget {
|
||||
final String title;
|
||||
final Object error;
|
||||
final Future Function()? refresh;
|
||||
|
||||
const TrainInfoErrorShared({required this.title, required this.error, this.refresh, super.key,});
|
||||
}
|
||||
|
||||
class TrainInfoBody extends ConsumerWidget {
|
||||
final TrainData trainData;
|
||||
final void Function()? onViewYesterdayTrain;
|
||||
final Future Function()? refresh;
|
||||
final bool? isRefreshing;
|
||||
|
||||
const TrainInfoBody({
|
||||
required this.trainData,
|
||||
this.onViewYesterdayTrain,
|
||||
this.refresh,
|
||||
this.isRefreshing,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final uiDesign = ref.watch(uiDesignProvider);
|
||||
|
||||
switch (uiDesign) {
|
||||
case UiDesign.MATERIAL:
|
||||
return TrainInfoBodyMaterial(
|
||||
trainData: trainData,
|
||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||
refresh: refresh,
|
||||
isRefreshing: isRefreshing,
|
||||
);
|
||||
case UiDesign.CUPERTINO:
|
||||
return TrainInfoBodyCupertino(
|
||||
trainData: trainData,
|
||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||
refresh: refresh,
|
||||
isRefreshing: isRefreshing,
|
||||
);
|
||||
case UiDesign.FLUENT:
|
||||
return TrainInfoBodyFluent(
|
||||
trainData: trainData,
|
||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||
refresh: refresh,
|
||||
isRefreshing: isRefreshing,
|
||||
);
|
||||
default:
|
||||
throw UnmatchedUiDesignException(uiDesign);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class TrainInfoBodyShared extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
final void Function()? onViewYesterdayTrain;
|
||||
final Future Function()? refresh;
|
||||
final bool? isRefreshing;
|
||||
|
||||
const TrainInfoBodyShared({
|
||||
required this.trainData,
|
||||
this.onViewYesterdayTrain,
|
||||
this.refresh,
|
||||
this.isRefreshing,
|
||||
super.key,
|
||||
});
|
||||
}
|
||||
|
||||
abstract class DisplayTrainYesterdayWarningCommon extends StatelessWidget {
|
||||
|
|
|
@ -10,7 +10,7 @@ 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_cupertino_DisplayTrainStation.dart';
|
||||
import 'package:info_tren/utils/state_to_string.dart';
|
||||
|
||||
class TrainInfoLoadingCupertino extends TrainInfoLoading {
|
||||
class TrainInfoLoadingCupertino extends TrainInfoLoadingShared {
|
||||
TrainInfoLoadingCupertino({required super.title, super.loadingText, super.key,});
|
||||
|
||||
@override
|
||||
|
@ -26,17 +26,13 @@ class TrainInfoLoadingCupertino extends TrainInfoLoading {
|
|||
}
|
||||
}
|
||||
|
||||
class TrainInfoErrorCupertino extends TrainInfoError {
|
||||
class TrainInfoErrorCupertino extends TrainInfoErrorShared {
|
||||
const TrainInfoErrorCupertino({
|
||||
required Object error,
|
||||
required String title,
|
||||
Future Function()? refresh,
|
||||
required super.error,
|
||||
required super.title,
|
||||
super.refresh,
|
||||
super.key,
|
||||
}) : super(
|
||||
error: error,
|
||||
title: title,
|
||||
refresh: refresh,
|
||||
);
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
@ -64,17 +60,12 @@ class TrainInfoErrorCupertino extends TrainInfoError {
|
|||
}
|
||||
}
|
||||
|
||||
class TrainInfoCupertino extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
final Future Function()? refresh;
|
||||
final bool? isRefreshing;
|
||||
final void Function()? onViewYesterdayTrain;
|
||||
|
||||
class TrainInfoCupertino extends TrainInfoShared {
|
||||
const TrainInfoCupertino({
|
||||
required this.trainData,
|
||||
this.refresh,
|
||||
this.isRefreshing,
|
||||
this.onViewYesterdayTrain,
|
||||
required super.trainData,
|
||||
super.refresh,
|
||||
super.isRefreshing,
|
||||
super.onViewYesterdayTrain,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
@ -93,195 +84,10 @@ class TrainInfoCupertino extends StatelessWidget {
|
|||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Builder(builder: (context) {
|
||||
final topPadding = MediaQuery.of(context).padding.top;
|
||||
|
||||
return NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
||||
return [
|
||||
// SliverPadding(
|
||||
// padding: EdgeInsets.only(
|
||||
// top: topPadding,
|
||||
// ),
|
||||
// ),
|
||||
SliverPersistentHeaderPadding(
|
||||
maxHeight: topPadding,
|
||||
)
|
||||
];
|
||||
},
|
||||
body: Builder(builder: (context) {
|
||||
return CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
if (refresh != null)
|
||||
CupertinoSliverRefreshControl(
|
||||
builder: (context, mode, pulledExtent,
|
||||
refreshTriggerPullDistance, refreshIndicatorExtent) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: pulledExtent,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: min(
|
||||
refreshIndicatorExtent, pulledExtent),
|
||||
child: Center(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (mode ==
|
||||
RefreshIndicatorMode.inactive) {
|
||||
return Container();
|
||||
} else if (mode ==
|
||||
RefreshIndicatorMode.done) {
|
||||
return const Text('Refreshed!');
|
||||
} else if (mode ==
|
||||
RefreshIndicatorMode.drag) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
CupertinoActivityIndicator(
|
||||
animating: false,
|
||||
),
|
||||
Text('Pull to refresh...'),
|
||||
],
|
||||
);
|
||||
} else if (mode ==
|
||||
RefreshIndicatorMode.armed) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
CupertinoActivityIndicator(
|
||||
animating: false,
|
||||
),
|
||||
Text('Release to refresh...'),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
CupertinoActivityIndicator(),
|
||||
Text('Refreshing'),
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
onRefresh: refresh,
|
||||
),
|
||||
DisplayTrainID(
|
||||
child: TrainInfoBody(
|
||||
trainData: trainData,
|
||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||
),
|
||||
DisplayTrainOperator(
|
||||
trainData: trainData,
|
||||
),
|
||||
DisplayTrainRoute(
|
||||
trainData: trainData,
|
||||
),
|
||||
DisplayTrainDeparture(
|
||||
trainData: trainData,
|
||||
),
|
||||
const SliverToBoxAdapter(
|
||||
child: CupertinoDivider(
|
||||
color: foregroundWhite,
|
||||
),
|
||||
),
|
||||
DisplayTrainLastInfo(
|
||||
trainData: trainData,
|
||||
),
|
||||
const SliverToBoxAdapter(
|
||||
child: CupertinoDivider(),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
// Expanded(
|
||||
// child: DisplayTrainNextStop(trainData: trainData,),
|
||||
// ),
|
||||
Expanded(
|
||||
child: DisplayTrainRouteDuration(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
// Expanded(
|
||||
// child: DisplayTrainDestination(trainData: trainData,),
|
||||
// ),
|
||||
const SizedBox(
|
||||
height: double.infinity,
|
||||
child: CupertinoVerticalDivider(),
|
||||
),
|
||||
Expanded(
|
||||
child: DisplayTrainRouteDistance(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// SliverToBoxAdapter(
|
||||
// child: CupertinoDivider(),
|
||||
// ),
|
||||
// SliverToBoxAdapter(
|
||||
// child: IntrinsicHeight(
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// // Expanded(
|
||||
// // child: DisplayTrainRouteDuration(trainData: trainData,),
|
||||
// // ),
|
||||
// Expanded(child: Container(),),
|
||||
// SizedBox(
|
||||
// height: double.infinity,
|
||||
// child: CupertinoVerticalDivider(),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: DisplayTrainRouteDistance(trainData: trainData,),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
const SliverToBoxAdapter(
|
||||
child: CupertinoDivider(
|
||||
color: foregroundWhite,
|
||||
),
|
||||
),
|
||||
if (onViewYesterdayTrain != null && trainData.stations.first.departure!.scheduleTime.compareTo(DateTime.now()) > 0) ...[
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainYesterdayWarningCupertino(onViewYesterdayTrain!),
|
||||
),
|
||||
const SliverToBoxAdapter(
|
||||
child: CupertinoDivider(
|
||||
color: foregroundWhite,
|
||||
),
|
||||
),
|
||||
],
|
||||
DisplayTrainStations(
|
||||
trainData: trainData,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).viewPadding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -390,14 +196,366 @@ class TrainInfoCupertino extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
class TrainInfoBodyCupertino extends TrainInfoBodyShared {
|
||||
const TrainInfoBodyCupertino({
|
||||
super.key,
|
||||
required super.trainData,
|
||||
super.onViewYesterdayTrain,
|
||||
super.isRefreshing,
|
||||
super.refresh,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mq = MediaQuery.of(context);
|
||||
final topPadding = mq.padding.top;
|
||||
|
||||
if (mq.orientation == Orientation.landscape && mq.size.width >= 1000) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: 400,
|
||||
maxWidth: 400,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
DisplayTrainID(trainData: trainData),
|
||||
DisplayTrainOperator(trainData: trainData),
|
||||
DisplayTrainRoute(trainData: trainData),
|
||||
DisplayTrainDeparture(trainData: trainData),
|
||||
const CupertinoDivider(
|
||||
color: foregroundWhite,
|
||||
),
|
||||
DisplayTrainLastInfo(trainData: trainData),
|
||||
const CupertinoDivider(),
|
||||
IntrinsicHeight(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
// Expanded(
|
||||
// child: DisplayTrainNextStop(trainData: trainData,),
|
||||
// ),
|
||||
Expanded(
|
||||
child: DisplayTrainRouteDuration(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
// Expanded(
|
||||
// child: DisplayTrainDestination(trainData: trainData,),
|
||||
// ),
|
||||
const SizedBox(
|
||||
height: double.infinity,
|
||||
child: CupertinoVerticalDivider(),
|
||||
),
|
||||
Expanded(
|
||||
child: DisplayTrainRouteDistance(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const CupertinoDivider(
|
||||
color: foregroundWhite,
|
||||
),
|
||||
if (onViewYesterdayTrain != null && trainData.stations.first.departure!.scheduleTime.compareTo(DateTime.now()) > 0) ...[
|
||||
DisplayTrainYesterdayWarningCupertino(onViewYesterdayTrain!),
|
||||
const CupertinoDivider(
|
||||
color: foregroundWhite,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
||||
return [
|
||||
// SliverPadding(
|
||||
// padding: EdgeInsets.only(
|
||||
// top: topPadding,
|
||||
// ),
|
||||
// ),
|
||||
SliverPersistentHeaderPadding(
|
||||
maxHeight: topPadding,
|
||||
)
|
||||
];
|
||||
},
|
||||
body: CustomScrollView(
|
||||
slivers: [
|
||||
if (refresh != null)
|
||||
CupertinoSliverRefreshControl(
|
||||
builder: (context, mode, pulledExtent,
|
||||
refreshTriggerPullDistance, refreshIndicatorExtent) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: pulledExtent,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: min(
|
||||
refreshIndicatorExtent, pulledExtent),
|
||||
child: Center(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (mode ==
|
||||
RefreshIndicatorMode.inactive) {
|
||||
return Container();
|
||||
} else if (mode ==
|
||||
RefreshIndicatorMode.done) {
|
||||
return const Text('Refreshed!');
|
||||
} else if (mode ==
|
||||
RefreshIndicatorMode.drag) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
CupertinoActivityIndicator(
|
||||
animating: false,
|
||||
),
|
||||
Text('Pull to refresh...'),
|
||||
],
|
||||
);
|
||||
} else if (mode ==
|
||||
RefreshIndicatorMode.armed) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
CupertinoActivityIndicator(
|
||||
animating: false,
|
||||
),
|
||||
Text('Release to refresh...'),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
CupertinoActivityIndicator(),
|
||||
Text('Refreshing'),
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
onRefresh: refresh,
|
||||
),
|
||||
|
||||
DisplayTrainStations(
|
||||
trainData: trainData,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).viewPadding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return NestedScrollView(
|
||||
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
||||
return [
|
||||
// SliverPadding(
|
||||
// padding: EdgeInsets.only(
|
||||
// top: topPadding,
|
||||
// ),
|
||||
// ),
|
||||
SliverPersistentHeaderPadding(
|
||||
maxHeight: topPadding,
|
||||
)
|
||||
];
|
||||
},
|
||||
body: Builder(builder: (context) {
|
||||
return CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
if (refresh != null)
|
||||
CupertinoSliverRefreshControl(
|
||||
builder: (context, mode, pulledExtent,
|
||||
refreshTriggerPullDistance, refreshIndicatorExtent) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: pulledExtent,
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: min(
|
||||
refreshIndicatorExtent, pulledExtent),
|
||||
child: Center(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (mode ==
|
||||
RefreshIndicatorMode.inactive) {
|
||||
return Container();
|
||||
} else if (mode ==
|
||||
RefreshIndicatorMode.done) {
|
||||
return const Text('Refreshed!');
|
||||
} else if (mode ==
|
||||
RefreshIndicatorMode.drag) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
CupertinoActivityIndicator(
|
||||
animating: false,
|
||||
),
|
||||
Text('Pull to refresh...'),
|
||||
],
|
||||
);
|
||||
} else if (mode ==
|
||||
RefreshIndicatorMode.armed) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
CupertinoActivityIndicator(
|
||||
animating: false,
|
||||
),
|
||||
Text('Release to refresh...'),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
CupertinoActivityIndicator(),
|
||||
Text('Refreshing'),
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
onRefresh: refresh,
|
||||
),
|
||||
...[
|
||||
DisplayTrainID(
|
||||
trainData: trainData,
|
||||
),
|
||||
DisplayTrainOperator(
|
||||
trainData: trainData,
|
||||
),
|
||||
DisplayTrainRoute(
|
||||
trainData: trainData,
|
||||
),
|
||||
DisplayTrainDeparture(
|
||||
trainData: trainData,
|
||||
),
|
||||
const CupertinoDivider(
|
||||
color: foregroundWhite,
|
||||
),
|
||||
DisplayTrainLastInfo(
|
||||
trainData: trainData,
|
||||
),
|
||||
const CupertinoDivider(),
|
||||
IntrinsicHeight(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
// Expanded(
|
||||
// child: DisplayTrainNextStop(trainData: trainData,),
|
||||
// ),
|
||||
Expanded(
|
||||
child: DisplayTrainRouteDuration(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
// Expanded(
|
||||
// child: DisplayTrainDestination(trainData: trainData,),
|
||||
// ),
|
||||
const SizedBox(
|
||||
height: double.infinity,
|
||||
child: CupertinoVerticalDivider(),
|
||||
),
|
||||
Expanded(
|
||||
child: DisplayTrainRouteDistance(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const CupertinoDivider(
|
||||
color: foregroundWhite,
|
||||
),
|
||||
if (onViewYesterdayTrain != null && trainData.stations.first.departure!.scheduleTime.compareTo(DateTime.now()) > 0) ...[
|
||||
DisplayTrainYesterdayWarningCupertino(onViewYesterdayTrain!),
|
||||
const CupertinoDivider(
|
||||
color: foregroundWhite,
|
||||
),
|
||||
],
|
||||
].map((e) => SliverToBoxAdapter(child: e)),
|
||||
// SliverToBoxAdapter(
|
||||
// child: CupertinoDivider(),
|
||||
// ),
|
||||
// SliverToBoxAdapter(
|
||||
// child: IntrinsicHeight(
|
||||
// child: Row(
|
||||
// children: <Widget>[
|
||||
// // Expanded(
|
||||
// // child: DisplayTrainRouteDuration(trainData: trainData,),
|
||||
// // ),
|
||||
// Expanded(child: Container(),),
|
||||
// SizedBox(
|
||||
// height: double.infinity,
|
||||
// child: CupertinoVerticalDivider(),
|
||||
// ),
|
||||
// Expanded(
|
||||
// child: DisplayTrainRouteDistance(trainData: trainData,),
|
||||
// )
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
DisplayTrainStations(
|
||||
trainData: trainData,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).viewPadding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainID extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
const DisplayTrainID({required this.trainData, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Center(
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text.rich(
|
||||
|
@ -416,7 +574,6 @@ class DisplayTrainID extends StatelessWidget {
|
|||
style: CupertinoTheme.of(context).textTheme.navLargeTitleTextStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -428,8 +585,7 @@ class DisplayTrainRoute extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Row(
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
|
@ -462,7 +618,6 @@ class DisplayTrainRoute extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -474,8 +629,7 @@ class DisplayTrainOperator extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Center(
|
||||
return Center(
|
||||
child: Text(
|
||||
trainData.operator,
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle.copyWith(
|
||||
|
@ -483,7 +637,6 @@ class DisplayTrainOperator extends StatelessWidget {
|
|||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -495,8 +648,7 @@ class DisplayTrainDeparture extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Text(
|
||||
// "Plecare în ${dataPlecare.day.toString().padLeft(2, '0')}.${dataPlecare.month.toString().padLeft(2, '0')}.${dataPlecare.year.toString().padLeft(4, '0')}",
|
||||
|
@ -507,7 +659,6 @@ class DisplayTrainDeparture extends StatelessWidget {
|
|||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -520,13 +671,10 @@ class DisplayTrainLastInfo extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (trainData.status == null) {
|
||||
return SliverToBoxAdapter(
|
||||
child: Container(),
|
||||
);
|
||||
return Container();
|
||||
}
|
||||
|
||||
return SliverToBoxAdapter(
|
||||
child: Column(
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Center(
|
||||
|
@ -603,7 +751,6 @@ class DisplayTrainLastInfo extends StatelessWidget {
|
|||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:info_tren/components/badge.dart';
|
||||
import 'package:info_tren/components/badge/badge.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
|
||||
class DisplayTrainStation extends StatelessWidget {
|
||||
|
@ -47,7 +47,7 @@ class DisplayTrainStation extends StatelessWidget {
|
|||
final isOnTime = delay <= 0 && real == true;
|
||||
const isNotScheduled = false;
|
||||
|
||||
return CupertinoBadge(
|
||||
return Badge(
|
||||
text: station.km.toString(),
|
||||
caption: 'km',
|
||||
isNotScheduled: isNotScheduled,
|
||||
|
@ -65,7 +65,7 @@ class DisplayTrainStation extends StatelessWidget {
|
|||
flex: 1,
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: station.platform == null ? Container() : CupertinoBadge(text: station.platform!, caption: 'linia'),
|
||||
child: station.platform == null ? Container() : Badge(text: station.platform!, caption: 'linia'),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
807
lib/pages/train_info_page/view_train/train_info_fluent.dart
Normal file
807
lib/pages/train_info_page/view_train/train_info_fluent.dart
Normal file
|
@ -0,0 +1,807 @@
|
|||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:info_tren/models.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_fluent_DisplayTrainStation.dart';
|
||||
import 'package:info_tren/utils/state_to_string.dart';
|
||||
|
||||
class TrainInfoLoadingFluent extends TrainInfoLoadingShared {
|
||||
TrainInfoLoadingFluent({required super.title, super.loadingText, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return NavigationView(
|
||||
appBar: NavigationAppBar(
|
||||
title: Text(title),
|
||||
),
|
||||
content: Center(
|
||||
child: loadingWidget,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrainInfoErrorFluent extends TrainInfoErrorShared {
|
||||
const TrainInfoErrorFluent({
|
||||
required super.error,
|
||||
required super.title,
|
||||
super.refresh,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return NavigationView(
|
||||
appBar: NavigationAppBar(
|
||||
title: Text(title),
|
||||
),
|
||||
content: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(error.toString()),
|
||||
if (refresh != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Button(
|
||||
child: const Text('Retry'),
|
||||
onPressed: () => refresh!(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrainInfoFluent extends TrainInfoShared {
|
||||
const TrainInfoFluent({
|
||||
super.key,
|
||||
required super.trainData,
|
||||
super.isRefreshing,
|
||||
super.refresh,
|
||||
super.onViewYesterdayTrain,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Builder(
|
||||
builder: (context) {
|
||||
return NavigationView(
|
||||
appBar: NavigationAppBar(
|
||||
title: Text(
|
||||
"Informații despre ${trainData.rank} ${trainData.number}",
|
||||
),
|
||||
actions: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (refresh != null) ...[
|
||||
Center(
|
||||
child: SizedBox(
|
||||
height: 32,
|
||||
width: 32,
|
||||
child: IconButton(
|
||||
icon: isRefreshing == true ? const ProgressRing() : const Icon(
|
||||
FluentIcons.refresh,
|
||||
size: 24,
|
||||
),
|
||||
onPressed: isRefreshing == true ? null : () {
|
||||
refresh!();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
content: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: TrainInfoBody(
|
||||
trainData: trainData,
|
||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrainInfoBodyFluent extends TrainInfoBodyShared {
|
||||
const TrainInfoBodyFluent({
|
||||
super.key,
|
||||
required super.trainData,
|
||||
super.onViewYesterdayTrain,
|
||||
super.refresh,
|
||||
super.isRefreshing,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mq = MediaQuery.of(context);
|
||||
|
||||
if (mq.orientation == Orientation.landscape && mq.size.width >= 1000) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: 400,
|
||||
maxWidth: 400,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
DisplayTrainID(trainData: trainData),
|
||||
DisplayTrainOperator(trainData: trainData),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2.0),
|
||||
child: DisplayTrainRoute(trainData: trainData),
|
||||
),
|
||||
DisplayTrainDeparture(trainData: trainData),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: DisplayTrainLastInfo(trainData: trainData),
|
||||
),
|
||||
IntrinsicHeight(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: DisplayTrainRouteDuration(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: DisplayTrainRouteDistance(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
if (onViewYesterdayTrain != null &&
|
||||
trainData.stations.first.departure!.scheduleTime
|
||||
.compareTo(DateTime.now()) >
|
||||
0)
|
||||
...[
|
||||
DisplayTrainYesterdayWarningFluent(
|
||||
onViewYesterdayTrain!,
|
||||
),
|
||||
const Divider(),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
DisplayTrainStations(
|
||||
trainData: trainData,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery
|
||||
.of(context)
|
||||
.viewPadding
|
||||
.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
return CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainID(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainOperator(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.only(left: 2, right: 2),
|
||||
sliver: SliverToBoxAdapter(
|
||||
child: DisplayTrainRoute(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainDeparture(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainLastInfo(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: DisplayTrainRouteDuration(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: DisplayTrainRouteDistance(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SliverToBoxAdapter(
|
||||
child: Divider(),
|
||||
),
|
||||
if (onViewYesterdayTrain != null &&
|
||||
trainData.stations.first.departure!.scheduleTime
|
||||
.compareTo(DateTime.now()) >
|
||||
0) ...[
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainYesterdayWarningFluent(
|
||||
onViewYesterdayTrain!),
|
||||
),
|
||||
const SliverToBoxAdapter(
|
||||
child: Divider(),
|
||||
),
|
||||
],
|
||||
DisplayTrainStations(
|
||||
trainData: trainData,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery
|
||||
.of(context)
|
||||
.viewPadding
|
||||
.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainID extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
const DisplayTrainID({required this.trainData, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: trainData.rank,
|
||||
style: TextStyle(
|
||||
color: trainData.rank.startsWith('IR')
|
||||
? const Color.fromARGB(255, 255, 0, 0)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(
|
||||
text: trainData.number,
|
||||
),
|
||||
],
|
||||
),
|
||||
style: FluentTheme.of(context).typography.title?.copyWith(
|
||||
color: FluentTheme.of(context).typography.body?.color,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainOperator extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
const DisplayTrainOperator({required this.trainData, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
trainData.operator,
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontSize: 14,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainRoute extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
const DisplayTrainRoute({required this.trainData, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
trainData.route.from,
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Center(child: Text("-")),
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
trainData.route.to,
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainDeparture extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
const DisplayTrainDeparture({required this.trainData, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Text(
|
||||
// "Plecare în ${dataPlecare.day.toString().padLeft(2, '0')}.${dataPlecare.month.toString().padLeft(2, '0')}.${dataPlecare.year.toString().padLeft(4, '0')}",
|
||||
"Plecare în ${trainData.date}",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontStyle: FontStyle.italic,
|
||||
fontWeight: FontWeight.w200,
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainLastInfo extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
const DisplayTrainLastInfo({required this.trainData, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (trainData.status == null) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Text(
|
||||
"Ultima informație",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
trainData.status!.station,
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 18,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Container(),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
stateToString(trainData.status!.state),
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 18,
|
||||
),
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Container(),
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final data = trainData.status!.delay;
|
||||
if (data == 0) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
if (data > 0) {
|
||||
return Text(
|
||||
"$data ${data == 1 ? 'minut' : 'minute'} întârziere",
|
||||
style:
|
||||
FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 16,
|
||||
color: Colors.red.lighter,
|
||||
// color: Colors.red.shade300,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Text(
|
||||
"${-data} ${data == -1 ? 'minut' : 'minute'} mai devreme",
|
||||
style:
|
||||
FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 16,
|
||||
color: Colors.green.lighter,
|
||||
// color: Colors.green.shade300,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainDestination extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
const DisplayTrainDestination({required this.trainData, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final destination = trainData.stations.last;
|
||||
|
||||
return Card(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(4),
|
||||
child: Text(
|
||||
"Destinația",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 0, 4, 0),
|
||||
child: Text(
|
||||
destination.name,
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
final arrival = destination.arrival!.scheduleTime.toLocal();
|
||||
final delay =
|
||||
trainData.stations.last.arrival!.status?.delay ?? 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"];
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
// Text(
|
||||
// "în ${arrival.day} ${months[arrival.month - 1]} ${arrival.year}",
|
||||
// style: Theme.of(context).textTheme.bodyText2?.copyWith(
|
||||
// fontSize: isSmallScreen(context) ? 12 : 14,
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
// ),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
text: 'la',
|
||||
children: [
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(
|
||||
text:
|
||||
'${arrival.hour.toString().padLeft(2, '0')}:${arrival.minute.toString().padLeft(2, '0')}',
|
||||
style: delay == 0
|
||||
? null
|
||||
: const TextStyle(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
if (delay != 0) ...[
|
||||
const TextSpan(text: ' '),
|
||||
TextSpan(
|
||||
text: arrivalWithDelayString,
|
||||
style: TextStyle(
|
||||
color: delay > 0
|
||||
? Colors.red.lighter
|
||||
: Colors.green.lighter,
|
||||
// color: delay > 0
|
||||
// ? Colors.red.shade300
|
||||
// : Colors.green.shade300,
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 16,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainRouteDistance extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
const DisplayTrainRouteDistance({required this.trainData, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Distanța rutei",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Text(
|
||||
"${trainData.stations.last.km} km",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 20,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainRouteDuration extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
|
||||
const DisplayTrainRouteDuration({required this.trainData, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"Durata rutei",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
var duration = trainData.stations.last.arrival!.scheduleTime
|
||||
.difference(
|
||||
trainData.stations.first.departure!.scheduleTime);
|
||||
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: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 20,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainYesterdayWarningFluent
|
||||
extends DisplayTrainYesterdayWarningCommon {
|
||||
const DisplayTrainYesterdayWarningFluent(super.onViewYesterdayTrain, {super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
const TextSpan(
|
||||
text: DisplayTrainYesterdayWarningCommon.trainDidNotDepart,
|
||||
),
|
||||
const TextSpan(text: '\n'),
|
||||
TextSpan(
|
||||
text: DisplayTrainYesterdayWarningCommon.seeYesterdayTrain,
|
||||
style: TextStyle(
|
||||
color: Colors.blue,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = onViewYesterdayTrain,
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainStations extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
const DisplayTrainStations({required this.trainData, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return IndexedSemantics(
|
||||
index: index,
|
||||
child: DisplayTrainStation(
|
||||
station: trainData.stations[index],
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
ViewStationPage.routeName,
|
||||
arguments: ViewStationArguments(stationName: trainData.stations[index].name),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
childCount: trainData.stations.length,
|
||||
addSemanticIndexes: true,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,447 @@
|
|||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:info_tren/components/badge/badge.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
|
||||
class DisplayTrainStation extends StatelessWidget {
|
||||
final Station station;
|
||||
final void Function()? onTap;
|
||||
|
||||
const DisplayTrainStation({required this.station, this.onTap, super.key,});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: HoverButton(
|
||||
onPressed: onTap,
|
||||
builder: (context, states) {
|
||||
return Card(
|
||||
padding: const EdgeInsets.all(2),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final departureStatus = station.departure?.status;
|
||||
final arrivalStatus = station.arrival?.status;
|
||||
int delay;
|
||||
bool real;
|
||||
if (departureStatus == null) {
|
||||
delay = arrivalStatus?.delay ?? 0;
|
||||
real = arrivalStatus?.real ?? false;
|
||||
}
|
||||
else if (arrivalStatus == null) {
|
||||
delay = departureStatus.delay;
|
||||
real = departureStatus.real;
|
||||
}
|
||||
else {
|
||||
delay = departureStatus.delay;
|
||||
real = departureStatus.real;
|
||||
if (!real && arrivalStatus.real) {
|
||||
delay = arrivalStatus.delay;
|
||||
real = arrivalStatus.real;
|
||||
}
|
||||
}
|
||||
|
||||
final isDelayed = delay > 0 && real == true;
|
||||
final isOnTime = delay <= 0 && real == true;
|
||||
const isNotScheduled = false;
|
||||
|
||||
return Badge(
|
||||
text: station.km.toString(),
|
||||
caption: 'km',
|
||||
isNotScheduled: isNotScheduled,
|
||||
isDelayed: isDelayed,
|
||||
isOnTime: isOnTime,
|
||||
);
|
||||
}
|
||||
),
|
||||
),
|
||||
),
|
||||
Title(
|
||||
station: station,
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: (station.platform == null)
|
||||
? Container()
|
||||
: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Badge(text: station.platform!, caption: 'linia',),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Time(
|
||||
station: station,
|
||||
),
|
||||
Delay(
|
||||
station: station,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Title extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
const Title({
|
||||
required this.station,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Text(
|
||||
station.name,
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 22,
|
||||
fontWeight: MediaQuery.of(context).boldText ? FontWeight.w500 : FontWeight.w300,
|
||||
// fontStyle: items[1] == "ONI" ? FontStyle.italic : FontStyle.normal,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class Time extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
const Time({
|
||||
required this.station,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.arrival == null) {
|
||||
// Plecare
|
||||
return DepartureTime(
|
||||
station: station,
|
||||
firstStation: true,
|
||||
);
|
||||
}
|
||||
|
||||
if (station.departure == null) {
|
||||
// Sosire
|
||||
return ArrivalTime(
|
||||
station: station,
|
||||
finalStation: true,
|
||||
);
|
||||
}
|
||||
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"→",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
Container(width: 2,),
|
||||
ArrivalTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
StopTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
DepartureTime(station: station,),
|
||||
Container(width: 2,),
|
||||
Text(
|
||||
"→",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ArrivalTime extends StatelessWidget {
|
||||
final Station station;
|
||||
final bool finalStation;
|
||||
|
||||
const ArrivalTime({
|
||||
required this.station,
|
||||
this.finalStation = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.arrival == null) {
|
||||
return Container();
|
||||
}
|
||||
if (finalStation) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"→",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
Container(width: 2,),
|
||||
const Text("sosire la "),
|
||||
ArrivalTime(station: station,),
|
||||
Expanded(child: Container(),),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final delay = station.arrival!.status?.delay ?? 0;
|
||||
final time = station.arrival!.scheduleTime.toLocal();
|
||||
|
||||
if (delay == 0) {
|
||||
return Text("${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
|
||||
}
|
||||
else if (delay > 0) {
|
||||
final oldDate = time;
|
||||
final newDate = oldDate.add(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
// color: Colors.red.shade300,
|
||||
color: Colors.red.lighter,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final oldDate = time;
|
||||
final newDate = oldDate.add(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
// color: Colors.green.shade300,
|
||||
color: Colors.green.lighter,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StopTime extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
const StopTime({
|
||||
required this.station,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
const Text(
|
||||
"staționează pentru",
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Builder(
|
||||
builder: (context) {
|
||||
int stopsForInt = station.stoppingTime!;
|
||||
bool minutes = false;
|
||||
if (stopsForInt >= 60) {
|
||||
stopsForInt ~/= 60;
|
||||
minutes = true;
|
||||
}
|
||||
if (stopsForInt == 1) {
|
||||
return Text(
|
||||
"1 ${minutes ? 'minut' : 'secundă'}",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else if (stopsForInt < 20) {
|
||||
return Text(
|
||||
"$stopsForInt ${minutes ? 'minute' : 'secunde'}",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Text(
|
||||
"$stopsForInt de ${minutes ? 'minute' : 'secunde'}",
|
||||
textAlign: TextAlign.center,
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DepartureTime extends StatelessWidget {
|
||||
final Station station;
|
||||
final bool firstStation;
|
||||
|
||||
const DepartureTime({
|
||||
required this.station,
|
||||
this.firstStation = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.departure == null) {
|
||||
return Container();
|
||||
}
|
||||
if (firstStation) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(child: Container(),),
|
||||
const Text("plecare la "),
|
||||
DepartureTime(station: station,),
|
||||
Container(width: 2,),
|
||||
Text(
|
||||
"→",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
fontSize: 22,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final delay = station.departure!.status?.delay ?? 0;
|
||||
final time = station.departure!.scheduleTime.toLocal();
|
||||
|
||||
if (delay == 0) {
|
||||
return Text("${time.hour.toString().padLeft(2, '0')}:${time.minute.toString().padLeft(2, '0')}");
|
||||
}
|
||||
else if (delay > 0) {
|
||||
final oldDate = time;
|
||||
final newDate = oldDate.add(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
// color: Colors.red.shade300,
|
||||
color: Colors.red.lighter,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
final oldDate = time;
|
||||
final newDate = oldDate.add(Duration(minutes: delay));
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"${oldDate.hour.toString().padLeft(2, '0')}:${oldDate.minute.toString().padLeft(2, '0')}",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
decoration: TextDecoration.lineThrough,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"${newDate.hour.toString().padLeft(2, '0')}:${newDate.minute.toString().padLeft(2, '0')}",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
// color: Colors.green.shade300,
|
||||
color: Colors.green.lighter,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Delay extends StatelessWidget {
|
||||
final Station station;
|
||||
|
||||
const Delay({
|
||||
required this.station,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (station.arrival?.status == null && station.departure?.status == null) {
|
||||
return Container();
|
||||
}
|
||||
var delay = station.arrival?.status?.delay;
|
||||
if (station.departure?.status?.real == true) {
|
||||
delay = station.departure?.status?.delay;
|
||||
}
|
||||
|
||||
if (delay == 0 || delay == null) {
|
||||
return Container();
|
||||
} else if (delay > 0) {
|
||||
return Text(
|
||||
"$delay ${delay == 1 ? 'minut' : 'minute'} întârziere",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
// color: Colors.red.shade300,
|
||||
color: Colors.red.lighter,
|
||||
fontSize: 14,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
);
|
||||
}
|
||||
else if (delay < 0) {
|
||||
return Text(
|
||||
"${-delay} ${delay == -1 ? 'minut' : 'minute'} mai devreme",
|
||||
style: FluentTheme.of(context).typography.body?.copyWith(
|
||||
// color: Colors.green.shade300,
|
||||
color: Colors.green.lighter,
|
||||
fontSize: 14,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Container();
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ 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/utils/state_to_string.dart';
|
||||
|
||||
class TrainInfoLoadingMaterial extends TrainInfoLoading {
|
||||
class TrainInfoLoadingMaterial extends TrainInfoLoadingShared {
|
||||
TrainInfoLoadingMaterial({required super.title, super.loadingText, super.key,});
|
||||
|
||||
@override
|
||||
|
@ -24,7 +24,7 @@ class TrainInfoLoadingMaterial extends TrainInfoLoading {
|
|||
}
|
||||
}
|
||||
|
||||
class TrainInfoErrorMaterial extends TrainInfoError {
|
||||
class TrainInfoErrorMaterial extends TrainInfoErrorShared {
|
||||
const TrainInfoErrorMaterial({
|
||||
required super.error,
|
||||
required super.title,
|
||||
|
@ -61,15 +61,12 @@ class TrainInfoErrorMaterial extends TrainInfoError {
|
|||
bool isSmallScreen(BuildContext context) =>
|
||||
MediaQuery.of(context).size.height <= 425;
|
||||
|
||||
class TrainInfoMaterial extends StatelessWidget {
|
||||
final TrainData trainData;
|
||||
final Future Function()? refresh;
|
||||
final void Function()? onViewYesterdayTrain;
|
||||
|
||||
class TrainInfoMaterial extends TrainInfoShared {
|
||||
const TrainInfoMaterial({
|
||||
required this.trainData,
|
||||
this.refresh,
|
||||
this.onViewYesterdayTrain,
|
||||
required super.trainData,
|
||||
super.refresh,
|
||||
super.onViewYesterdayTrain,
|
||||
super.isRefreshing,
|
||||
super.key,
|
||||
});
|
||||
|
||||
|
@ -83,7 +80,8 @@ class TrainInfoMaterial extends StatelessWidget {
|
|||
: AppBar(
|
||||
centerTitle: true,
|
||||
title: Text(
|
||||
"Informații despre ${trainData.rank} ${trainData.number}"),
|
||||
"Informații despre ${trainData.rank} ${trainData.number}",
|
||||
),
|
||||
),
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
|
@ -93,8 +91,8 @@ class TrainInfoMaterial extends StatelessWidget {
|
|||
left: false,
|
||||
right: false,
|
||||
child: SlimAppBar(
|
||||
title:
|
||||
'INFO TREN - ${trainData.rank} ${trainData.number}'),
|
||||
title: 'INFO TREN - ${trainData.rank} ${trainData.number}',
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SafeArea(
|
||||
|
@ -102,7 +100,112 @@ class TrainInfoMaterial extends StatelessWidget {
|
|||
top: isSmallScreen(context) ? false : true,
|
||||
child: RefreshIndicator(
|
||||
onRefresh: refresh ?? () async {},
|
||||
child: TrainInfoBody(
|
||||
trainData: trainData,
|
||||
onViewYesterdayTrain: onViewYesterdayTrain,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TrainInfoBodyMaterial extends TrainInfoBodyShared {
|
||||
const TrainInfoBodyMaterial({
|
||||
super.key,
|
||||
required super.trainData,
|
||||
super.onViewYesterdayTrain,
|
||||
super.isRefreshing,
|
||||
super.refresh,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mq = MediaQuery.of(context);
|
||||
|
||||
if (mq.orientation == Orientation.landscape && mq.size.width >= 1000) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Container(
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: 400,
|
||||
maxWidth: 400,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
DisplayTrainID(trainData: trainData),
|
||||
DisplayTrainOperator(trainData: trainData),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 2.0),
|
||||
child: DisplayTrainRoute(trainData: trainData),
|
||||
),
|
||||
DisplayTrainDeparture(trainData: trainData),
|
||||
DisplayTrainLastInfo(trainData: trainData),
|
||||
IntrinsicHeight(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: DisplayTrainRouteDuration(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: DisplayTrainRouteDistance(
|
||||
trainData: trainData,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Divider(
|
||||
color: Colors.white70,
|
||||
height: isSmallScreen(context) ? 8 : 16,
|
||||
),
|
||||
if (onViewYesterdayTrain != null &&
|
||||
trainData.stations.first.departure!.scheduleTime
|
||||
.compareTo(DateTime.now()) >
|
||||
0)
|
||||
...[
|
||||
DisplayTrainYesterdayWarningMaterial(
|
||||
onViewYesterdayTrain!,
|
||||
),
|
||||
Divider(
|
||||
color: Colors.white70,
|
||||
height: isSmallScreen(context) ? 8 : 16,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
DisplayTrainStations(
|
||||
trainData: trainData,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery
|
||||
.of(context)
|
||||
.viewPadding
|
||||
.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
else {
|
||||
return CustomScrollView(
|
||||
slivers: <Widget>[
|
||||
SliverToBoxAdapter(
|
||||
child: DisplayTrainID(
|
||||
|
@ -147,7 +250,8 @@ class TrainInfoMaterial extends StatelessWidget {
|
|||
Expanded(
|
||||
child: DisplayTrainRouteDuration(
|
||||
trainData: trainData,
|
||||
)),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: DisplayTrainRouteDistance(
|
||||
trainData: trainData,
|
||||
|
@ -194,20 +298,16 @@ class TrainInfoMaterial extends StatelessWidget {
|
|||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Container(
|
||||
height: MediaQuery.of(context).viewPadding.bottom,
|
||||
height: MediaQuery
|
||||
.of(context)
|
||||
.viewPadding
|
||||
.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DisplayTrainID extends StatelessWidget {
|
||||
|
@ -798,7 +898,7 @@ class DisplayTrainStations extends StatelessWidget {
|
|||
onTap: () {
|
||||
Navigator.of(context).pushNamed(
|
||||
ViewStationPage.routeName,
|
||||
arguments: trainData.stations[index].name,
|
||||
arguments: ViewStationArguments(stationName: trainData.stations[index].name),
|
||||
);
|
||||
},
|
||||
),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:info_tren/models.dart';
|
||||
import 'package:info_tren/components/badge.dart';
|
||||
import 'package:info_tren/components/badge/badge.dart';
|
||||
import 'package:info_tren/pages/train_info_page/view_train/train_info_material.dart';
|
||||
|
||||
class DisplayTrainStation extends StatelessWidget {
|
||||
|
@ -54,7 +54,7 @@ class DisplayTrainStation extends StatelessWidget {
|
|||
final isOnTime = delay <= 0 && real == true;
|
||||
const isNotScheduled = false;
|
||||
|
||||
return MaterialBadge(
|
||||
return Badge(
|
||||
text: station.km.toString(),
|
||||
caption: 'km',
|
||||
isNotScheduled: isNotScheduled,
|
||||
|
@ -74,7 +74,7 @@ class DisplayTrainStation extends StatelessWidget {
|
|||
? Container()
|
||||
: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: MaterialBadge(text: station.platform!, caption: 'linia',),
|
||||
child: Badge(text: station.platform!, caption: 'linia',),
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -6,6 +6,9 @@ UiDesign get defaultUiDesign {
|
|||
if (Platform.isIOS) {
|
||||
return UiDesign.CUPERTINO;
|
||||
}
|
||||
else if (Platform.isLinux || Platform.isWindows) {
|
||||
return UiDesign.FLUENT;
|
||||
}
|
||||
else {
|
||||
return UiDesign.MATERIAL;
|
||||
}
|
||||
|
|
44
pubspec.lock
44
pubspec.lock
|
@ -99,6 +99,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -162,6 +169,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
fluent_ui:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: fluent_ui
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.3+1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -181,6 +195,11 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_riverpod:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -256,6 +275,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.17.0"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -311,7 +337,7 @@ packages:
|
|||
name: material_color_utilities
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.1.5"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -424,6 +450,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
recase:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: recase
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
riverpod:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -431,6 +464,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
scroll_pos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: scroll_pos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -659,7 +699,7 @@ packages:
|
|||
name: vector_math
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.2"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -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.7.8
|
||||
version: 2.7.9
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.0 <3.0.0"
|
||||
|
@ -33,6 +33,7 @@ dependencies:
|
|||
freezed_annotation: ^2.2.0
|
||||
json_annotation: ^4.7.0
|
||||
shared_preferences: ^2.0.15
|
||||
fluent_ui: ^4.0.3+1
|
||||
|
||||
dev_dependencies:
|
||||
# flutter_test:
|
||||
|
|
Loading…
Add table
Reference in a new issue