Dan Cojocaru
3 years ago
19 changed files with 741 additions and 9 deletions
@ -0,0 +1 @@ |
|||||||
|
const authority = 'scraper.infotren.dcdevelop.xyz'; |
@ -0,0 +1,10 @@ |
|||||||
|
import 'dart:convert'; |
||||||
|
|
||||||
|
import 'package:http/http.dart' as http; |
||||||
|
import 'package:info_tren/api/common.dart'; |
||||||
|
import 'package:info_tren/models/station_data.dart'; |
||||||
|
|
||||||
|
Future<StationData> getStationData(String stationName) async { |
||||||
|
final response = await http.get(Uri.https(authority, 'v2/station/$stationName')); |
||||||
|
return StationData.fromJson(jsonDecode(response.body)); |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
import 'dart:convert'; |
||||||
|
|
||||||
|
import 'package:http/http.dart' as http; |
||||||
|
import 'package:info_tren/api/common.dart'; |
||||||
|
import 'package:info_tren/models/stations_result.dart'; |
||||||
|
|
||||||
|
Future<List<StationsResult>> get stations async { |
||||||
|
final result = await http.get(Uri.https(authority, 'v2/stations')); |
||||||
|
final data = jsonDecode(result.body) as List<dynamic>; |
||||||
|
return data.map((e) => StationsResult.fromJson(e)).toList(growable: false,); |
||||||
|
} |
@ -1,9 +1,8 @@ |
|||||||
import 'package:http/http.dart' as http; |
import 'package:http/http.dart' as http; |
||||||
|
import 'package:info_tren/api/common.dart'; |
||||||
import 'package:info_tren/models/train_data.dart'; |
import 'package:info_tren/models/train_data.dart'; |
||||||
|
|
||||||
const AUTHORITY = 'scraper.infotren.dcdevelop.xyz'; |
|
||||||
|
|
||||||
Future<TrainData> getTrain(String trainNumber) async { |
Future<TrainData> getTrain(String trainNumber) async { |
||||||
final response = await http.get(Uri.https(AUTHORITY, 'v2/train/$trainNumber')); |
final response = await http.get(Uri.https(authority, 'v2/train/$trainNumber')); |
||||||
return trainDataFromJson(response.body); |
return trainDataFromJson(response.body); |
||||||
} |
} |
@ -0,0 +1,43 @@ |
|||||||
|
import 'package:flutter/cupertino.dart'; |
||||||
|
|
||||||
|
class CupertinoListTile extends StatelessWidget { |
||||||
|
final Widget? leading; |
||||||
|
final Widget? title; |
||||||
|
final Widget? subtitle; |
||||||
|
final Widget? trailing; |
||||||
|
|
||||||
|
const CupertinoListTile({ Key? key, this.leading, this.title, this.subtitle, this.trailing, }) : super(key: key); |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return Row( |
||||||
|
mainAxisSize: MainAxisSize.max, |
||||||
|
children: [ |
||||||
|
if (leading != null) |
||||||
|
leading!, |
||||||
|
Expanded( |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
children: [ |
||||||
|
if (title != null) |
||||||
|
title!, |
||||||
|
if (subtitle != null) |
||||||
|
CupertinoTheme( |
||||||
|
child: subtitle!, |
||||||
|
data: CupertinoTheme.of(context).copyWith( |
||||||
|
textTheme: CupertinoTextThemeData( |
||||||
|
textStyle: TextStyle( |
||||||
|
fontSize: CupertinoTheme.of(context).textTheme.textStyle.fontSize! - 2, |
||||||
|
) |
||||||
|
) |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
if (trailing != null) |
||||||
|
trailing!, |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
import 'package:json_annotation/json_annotation.dart'; |
||||||
|
|
||||||
|
part 'station_data.g.dart'; |
||||||
|
|
||||||
|
@JsonSerializable() |
||||||
|
class StationData { |
||||||
|
final String date; |
||||||
|
final String stationName; |
||||||
|
final List<StationArrival>? arrivals; |
||||||
|
final List<StationDeparture>? departures; |
||||||
|
|
||||||
|
const StationData({required this.date, required this.stationName, required this.arrivals, required this.departures}); |
||||||
|
|
||||||
|
factory StationData.fromJson(Map<String, dynamic> json) => _$StationDataFromJson(json); |
||||||
|
Map<String, dynamic> toJson() => _$StationDataToJson(this); |
||||||
|
} |
||||||
|
|
||||||
|
@JsonSerializable() |
||||||
|
class StationArrival { |
||||||
|
final int? stoppingTime; |
||||||
|
final DateTime time; |
||||||
|
final StationTrainArr train; |
||||||
|
|
||||||
|
const StationArrival({required this.stoppingTime, required this.time, required this.train,}); |
||||||
|
|
||||||
|
factory StationArrival.fromJson(Map<String, dynamic> json) => _$StationArrivalFromJson(json); |
||||||
|
Map<String, dynamic> toJson() => _$StationArrivalToJson(this); |
||||||
|
} |
||||||
|
|
||||||
|
@JsonSerializable() |
||||||
|
class StationDeparture { |
||||||
|
final int? stoppingTime; |
||||||
|
final DateTime time; |
||||||
|
final StationTrainDep train; |
||||||
|
|
||||||
|
const StationDeparture({required this.stoppingTime, required this.time, required this.train,}); |
||||||
|
|
||||||
|
factory StationDeparture.fromJson(Map<String, dynamic> json) => _$StationDepartureFromJson(json); |
||||||
|
Map<String, dynamic> toJson() => _$StationDepartureToJson(this); |
||||||
|
} |
||||||
|
|
||||||
|
@JsonSerializable() |
||||||
|
class StationTrainArr { |
||||||
|
final String rank; |
||||||
|
final String number; |
||||||
|
final String operator; |
||||||
|
final String origin; |
||||||
|
final List<String>? route; |
||||||
|
|
||||||
|
StationTrainArr({required this.rank, required this.number, required this.operator, required this.origin, this.route,}); |
||||||
|
|
||||||
|
factory StationTrainArr.fromJson(Map<String, dynamic> json) => _$StationTrainArrFromJson(json); |
||||||
|
Map<String, dynamic> toJson() => _$StationTrainArrToJson(this); |
||||||
|
} |
||||||
|
|
||||||
|
@JsonSerializable() |
||||||
|
class StationTrainDep { |
||||||
|
final String rank; |
||||||
|
final String number; |
||||||
|
final String operator; |
||||||
|
final String destination; |
||||||
|
final List<String>? route; |
||||||
|
|
||||||
|
StationTrainDep({required this.rank, required this.number, required this.operator, required this.destination, this.route,}); |
||||||
|
|
||||||
|
factory StationTrainDep.fromJson(Map<String, dynamic> json) => _$StationTrainDepFromJson(json); |
||||||
|
Map<String, dynamic> toJson() => _$StationTrainDepToJson(this); |
||||||
|
} |
@ -0,0 +1,92 @@ |
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||||
|
|
||||||
|
part of 'station_data.dart'; |
||||||
|
|
||||||
|
// ************************************************************************** |
||||||
|
// JsonSerializableGenerator |
||||||
|
// ************************************************************************** |
||||||
|
|
||||||
|
StationData _$StationDataFromJson(Map<String, dynamic> json) => StationData( |
||||||
|
date: json['date'] as String, |
||||||
|
stationName: json['stationName'] as String, |
||||||
|
arrivals: (json['arrivals'] as List<dynamic>?) |
||||||
|
?.map((e) => StationArrival.fromJson(e as Map<String, dynamic>)) |
||||||
|
.toList(), |
||||||
|
departures: (json['departures'] as List<dynamic>?) |
||||||
|
?.map((e) => StationDeparture.fromJson(e as Map<String, dynamic>)) |
||||||
|
.toList(), |
||||||
|
); |
||||||
|
|
||||||
|
Map<String, dynamic> _$StationDataToJson(StationData instance) => |
||||||
|
<String, dynamic>{ |
||||||
|
'date': instance.date, |
||||||
|
'stationName': instance.stationName, |
||||||
|
'arrivals': instance.arrivals, |
||||||
|
'departures': instance.departures, |
||||||
|
}; |
||||||
|
|
||||||
|
StationArrival _$StationArrivalFromJson(Map<String, dynamic> json) => |
||||||
|
StationArrival( |
||||||
|
stoppingTime: json['stoppingTime'] as int?, |
||||||
|
time: DateTime.parse(json['time'] as String), |
||||||
|
train: StationTrainArr.fromJson(json['train'] as Map<String, dynamic>), |
||||||
|
); |
||||||
|
|
||||||
|
Map<String, dynamic> _$StationArrivalToJson(StationArrival instance) => |
||||||
|
<String, dynamic>{ |
||||||
|
'stoppingTime': instance.stoppingTime, |
||||||
|
'time': instance.time.toIso8601String(), |
||||||
|
'train': instance.train, |
||||||
|
}; |
||||||
|
|
||||||
|
StationDeparture _$StationDepartureFromJson(Map<String, dynamic> json) => |
||||||
|
StationDeparture( |
||||||
|
stoppingTime: json['stoppingTime'] as int?, |
||||||
|
time: DateTime.parse(json['time'] as String), |
||||||
|
train: StationTrainDep.fromJson(json['train'] as Map<String, dynamic>), |
||||||
|
); |
||||||
|
|
||||||
|
Map<String, dynamic> _$StationDepartureToJson(StationDeparture instance) => |
||||||
|
<String, dynamic>{ |
||||||
|
'stoppingTime': instance.stoppingTime, |
||||||
|
'time': instance.time.toIso8601String(), |
||||||
|
'train': instance.train, |
||||||
|
}; |
||||||
|
|
||||||
|
StationTrainArr _$StationTrainArrFromJson(Map<String, dynamic> json) => |
||||||
|
StationTrainArr( |
||||||
|
rank: json['rank'] as String, |
||||||
|
number: json['number'] as String, |
||||||
|
operator: json['operator'] as String, |
||||||
|
origin: json['origin'] as String, |
||||||
|
route: |
||||||
|
(json['route'] as List<dynamic>?)?.map((e) => e as String).toList(), |
||||||
|
); |
||||||
|
|
||||||
|
Map<String, dynamic> _$StationTrainArrToJson(StationTrainArr instance) => |
||||||
|
<String, dynamic>{ |
||||||
|
'rank': instance.rank, |
||||||
|
'number': instance.number, |
||||||
|
'operator': instance.operator, |
||||||
|
'origin': instance.origin, |
||||||
|
'route': instance.route, |
||||||
|
}; |
||||||
|
|
||||||
|
StationTrainDep _$StationTrainDepFromJson(Map<String, dynamic> json) => |
||||||
|
StationTrainDep( |
||||||
|
rank: json['rank'] as String, |
||||||
|
number: json['number'] as String, |
||||||
|
operator: json['operator'] as String, |
||||||
|
destination: json['destination'] as String, |
||||||
|
route: |
||||||
|
(json['route'] as List<dynamic>?)?.map((e) => e as String).toList(), |
||||||
|
); |
||||||
|
|
||||||
|
Map<String, dynamic> _$StationTrainDepToJson(StationTrainDep instance) => |
||||||
|
<String, dynamic>{ |
||||||
|
'rank': instance.rank, |
||||||
|
'number': instance.number, |
||||||
|
'operator': instance.operator, |
||||||
|
'destination': instance.destination, |
||||||
|
'route': instance.route, |
||||||
|
}; |
@ -0,0 +1,14 @@ |
|||||||
|
import 'package:json_annotation/json_annotation.dart'; |
||||||
|
|
||||||
|
part 'stations_result.g.dart'; |
||||||
|
|
||||||
|
@JsonSerializable() |
||||||
|
class StationsResult { |
||||||
|
final String name; |
||||||
|
final List<String>? stoppedAtBy; |
||||||
|
|
||||||
|
const StationsResult({required this.name, this.stoppedAtBy}); |
||||||
|
|
||||||
|
factory StationsResult.fromJson(Map<String, dynamic> json) => _$StationsResultFromJson(json); |
||||||
|
Map<String, dynamic> toJson() => _$StationsResultToJson(this); |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND |
||||||
|
|
||||||
|
part of 'stations_result.dart'; |
||||||
|
|
||||||
|
// ************************************************************************** |
||||||
|
// JsonSerializableGenerator |
||||||
|
// ************************************************************************** |
||||||
|
|
||||||
|
StationsResult _$StationsResultFromJson(Map<String, dynamic> json) => |
||||||
|
StationsResult( |
||||||
|
name: json['name'] as String, |
||||||
|
stoppedAtBy: (json['stoppedAtBy'] as List<dynamic>?) |
||||||
|
?.map((e) => e as String) |
||||||
|
.toList(), |
||||||
|
); |
||||||
|
|
||||||
|
Map<String, dynamic> _$StationsResultToJson(StationsResult instance) => |
||||||
|
<String, dynamic>{ |
||||||
|
'name': instance.name, |
||||||
|
'stoppedAtBy': instance.stoppedAtBy, |
||||||
|
}; |
@ -1,9 +1,53 @@ |
|||||||
import 'package:flutter/cupertino.dart'; |
import 'package:flutter/cupertino.dart'; |
||||||
|
import 'package:info_tren/components/cupertino_divider.dart'; |
||||||
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart'; |
import 'package:info_tren/pages/station_arrdep_page/select_station/select_station.dart'; |
||||||
|
|
||||||
class SelectStationPageStateCupertino extends SelectStationPageState { |
class SelectStationPageStateCupertino extends SelectStationPageState { |
||||||
@override |
@override |
||||||
Widget build(BuildContext context) { |
Widget build(BuildContext context) { |
||||||
return Container(); |
return CupertinoPageScaffold( |
||||||
|
navigationBar: CupertinoNavigationBar( |
||||||
|
middle: Text(SelectStationPageState.pageTitle), |
||||||
|
), |
||||||
|
child: SafeArea( |
||||||
|
bottom: false, |
||||||
|
child: Column( |
||||||
|
mainAxisSize: MainAxisSize.max, |
||||||
|
children: [ |
||||||
|
Padding( |
||||||
|
padding: const EdgeInsets.all(4), |
||||||
|
child: CupertinoTextField( |
||||||
|
controller: textEditingController, |
||||||
|
autofocus: true, |
||||||
|
placeholder: SelectStationPageState.textFieldLabel, |
||||||
|
textInputAction: TextInputAction.search, |
||||||
|
onChanged: onTextChanged, |
||||||
|
), |
||||||
|
), |
||||||
|
Expanded( |
||||||
|
child: ListView.builder( |
||||||
|
itemBuilder: (context, index) { |
||||||
|
return Column( |
||||||
|
mainAxisSize: MainAxisSize.min, |
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch, |
||||||
|
children: [ |
||||||
|
GestureDetector( |
||||||
|
child: Padding( |
||||||
|
padding: const EdgeInsets.all(8.0), |
||||||
|
child: Text(filteredStations[index]), |
||||||
|
), |
||||||
|
onTap: () => onSuggestionSelected(filteredStations[index]), |
||||||
|
), |
||||||
|
CupertinoDivider(), |
||||||
|
], |
||||||
|
); |
||||||
|
}, |
||||||
|
itemCount: filteredStations.length, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -0,0 +1,89 @@ |
|||||||
|
import 'package:flutter/widgets.dart'; |
||||||
|
import 'package:info_tren/api/station_data.dart'; |
||||||
|
import 'package:info_tren/components/refresh_future_builder.dart'; |
||||||
|
import 'package:info_tren/models/station_data.dart'; |
||||||
|
import 'package:info_tren/models/ui_design.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_material.dart'; |
||||||
|
import 'package:info_tren/pages/train_info_page/view_train/train_info.dart'; |
||||||
|
import 'package:info_tren/utils/default_ui_design.dart'; |
||||||
|
|
||||||
|
class ViewStationPage extends StatefulWidget { |
||||||
|
final UiDesign? uiDesign; |
||||||
|
final String stationName; |
||||||
|
|
||||||
|
const ViewStationPage({ Key? key, required this.stationName, this.uiDesign }) : super(key: key); |
||||||
|
|
||||||
|
static String routeName = '/stationArrDep/viewStation'; |
||||||
|
|
||||||
|
@override |
||||||
|
ViewStationPageState createState() { |
||||||
|
final uiDesign = this.uiDesign ?? defaultUiDesign; |
||||||
|
switch (uiDesign) { |
||||||
|
case UiDesign.MATERIAL: |
||||||
|
return ViewStationPageStateMaterial(); |
||||||
|
case UiDesign.CUPERTINO: |
||||||
|
return ViewStationPageStateCupertino(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
abstract class ViewStationPageState extends State<ViewStationPage> { |
||||||
|
static const arrivals = 'Sosiri'; |
||||||
|
static const departures = 'Pleacări'; |
||||||
|
static const loadingText = 'Se încarcă...'; |
||||||
|
static const arrivesFrom = 'Sosește de la'; |
||||||
|
static const departsTo = 'Pleacă către'; |
||||||
|
|
||||||
|
ViewStationPageTab tab = ViewStationPageTab.departures; |
||||||
|
late String stationName; |
||||||
|
late Future<StationData> Function() futureCreator; |
||||||
|
|
||||||
|
@override |
||||||
|
void initState() { |
||||||
|
initData(); |
||||||
|
super.initState(); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
void didChangeDependencies() { |
||||||
|
if (stationName != widget.stationName) { |
||||||
|
setState(() { |
||||||
|
initData(); |
||||||
|
}); |
||||||
|
} |
||||||
|
super.didChangeDependencies(); |
||||||
|
} |
||||||
|
|
||||||
|
void initData() { |
||||||
|
stationName = widget.stationName; |
||||||
|
futureCreator = () => getStationData(stationName); |
||||||
|
} |
||||||
|
|
||||||
|
Widget buildContent(BuildContext context, Future Function() refresh, RefreshFutureBuilderSnapshot<StationData> snapshot); |
||||||
|
Widget buildStationArrivalItem(BuildContext context, StationArrival item); |
||||||
|
Widget buildStationDepartureItem(BuildContext context, StationDeparture item); |
||||||
|
|
||||||
|
void onTabChange(int index) { |
||||||
|
setState(() { |
||||||
|
tab = ViewStationPageTab.values[index]; |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
void onTrainTapped(String trainNumber) { |
||||||
|
Navigator.of(context).pushNamed(TrainInfo.routeName, arguments: trainNumber); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
return RefreshFutureBuilder( |
||||||
|
futureCreator: futureCreator, |
||||||
|
builder: buildContent, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
enum ViewStationPageTab { |
||||||
|
arrivals, |
||||||
|
departures, |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
import 'package:flutter/cupertino.dart'; |
||||||
|
import 'package:info_tren/components/loading/loading.dart'; |
||||||
|
import 'package:info_tren/components/refresh_future_builder.dart'; |
||||||
|
import 'package:flutter/src/widgets/framework.dart'; |
||||||
|
import 'package:info_tren/components/sliver_persistent_header_padding.dart'; |
||||||
|
import 'package:info_tren/models/station_data.dart'; |
||||||
|
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart'; |
||||||
|
|
||||||
|
class ViewStationPageStateCupertino extends ViewStationPageState { |
||||||
|
@override |
||||||
|
Widget buildContent(BuildContext context, Future Function() refresh, RefreshFutureBuilderSnapshot<StationData> snapshot) { |
||||||
|
return CupertinoPageScaffold( |
||||||
|
navigationBar: CupertinoNavigationBar( |
||||||
|
middle: Text(snapshot.hasData ? snapshot.data!.stationName : stationName), |
||||||
|
), |
||||||
|
child: snapshot.hasData ? CupertinoTabScaffold( |
||||||
|
tabBar: CupertinoTabBar( |
||||||
|
items: [ |
||||||
|
BottomNavigationBarItem( |
||||||
|
icon: Icon(CupertinoIcons.arrow_down), |
||||||
|
label: ViewStationPageState.arrivals, |
||||||
|
), |
||||||
|
BottomNavigationBarItem( |
||||||
|
icon: Icon(CupertinoIcons.arrow_up), |
||||||
|
label: ViewStationPageState.departures, |
||||||
|
), |
||||||
|
], |
||||||
|
onTap: onTabChange, |
||||||
|
currentIndex: tab.index, |
||||||
|
), |
||||||
|
tabBuilder: (context, index) { |
||||||
|
final topPadding = MediaQuery.of(context).padding.top; |
||||||
|
return NestedScrollView( |
||||||
|
headerSliverBuilder: (context, _) { |
||||||
|
return [SliverPersistentHeaderPadding(maxHeight: topPadding)]; |
||||||
|
}, |
||||||
|
body: CustomScrollView( |
||||||
|
slivers: [ |
||||||
|
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, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
); |
||||||
|
}, |
||||||
|
) : snapshot.state == RefreshFutureBuilderState.waiting ? Loading(text: ViewStationPageState.loadingText, uiDesign: widget.uiDesign,) : Container(), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget buildStationArrivalItem(BuildContext context, StationArrival item) { |
||||||
|
return GestureDetector( |
||||||
|
onTap: () => onTrainTapped(item.train.number), |
||||||
|
child: CupertinoFormRow( |
||||||
|
child: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'), |
||||||
|
prefix: Text.rich( |
||||||
|
TextSpan( |
||||||
|
children: [ |
||||||
|
TextSpan( |
||||||
|
text: item.train.rank, |
||||||
|
style: TextStyle( |
||||||
|
color: item.train.rank.startsWith('IR') ? Color.fromARGB(255, 255, 0, 0) : null, |
||||||
|
), |
||||||
|
), |
||||||
|
TextSpan(text: ' '), |
||||||
|
TextSpan(text: item.train.number,), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
helper: Text.rich( |
||||||
|
TextSpan( |
||||||
|
children: [ |
||||||
|
TextSpan(text: ViewStationPageState.arrivesFrom), |
||||||
|
TextSpan(text: ' '), |
||||||
|
TextSpan(text: item.train.origin), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget buildStationDepartureItem(BuildContext context, StationDeparture item) { |
||||||
|
return GestureDetector( |
||||||
|
onTap: () => onTrainTapped(item.train.number), |
||||||
|
child: CupertinoFormRow( |
||||||
|
child: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'), |
||||||
|
prefix: Text.rich( |
||||||
|
TextSpan( |
||||||
|
children: [ |
||||||
|
TextSpan( |
||||||
|
text: item.train.rank, |
||||||
|
style: TextStyle( |
||||||
|
color: item.train.rank.startsWith('IR') ? Color.fromARGB(255, 255, 0, 0) : null, |
||||||
|
), |
||||||
|
), |
||||||
|
TextSpan(text: ' '), |
||||||
|
TextSpan(text: item.train.number,), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
helper: Text.rich( |
||||||
|
TextSpan( |
||||||
|
children: [ |
||||||
|
TextSpan(text: ViewStationPageState.departsTo), |
||||||
|
TextSpan(text: ' '), |
||||||
|
TextSpan(text: item.train.destination), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:info_tren/components/loading/loading.dart'; |
||||||
|
import 'package:info_tren/components/refresh_future_builder.dart'; |
||||||
|
import 'package:flutter/src/widgets/framework.dart'; |
||||||
|
import 'package:info_tren/models/station_data.dart'; |
||||||
|
import 'package:info_tren/pages/station_arrdep_page/view_station/view_station.dart'; |
||||||
|
|
||||||
|
class ViewStationPageStateMaterial extends ViewStationPageState { |
||||||
|
@override |
||||||
|
Widget buildContent(BuildContext context, Future Function() refresh, RefreshFutureBuilderSnapshot<StationData> snapshot) { |
||||||
|
return Scaffold( |
||||||
|
appBar: AppBar( |
||||||
|
title: Text(snapshot.hasData ? snapshot.data!.stationName : stationName), |
||||||
|
centerTitle: true, |
||||||
|
), |
||||||
|
body: snapshot.state == RefreshFutureBuilderState.waiting ? Loading(text: ViewStationPageState.loadingText, uiDesign: widget.uiDesign,) : CustomScrollView( |
||||||
|
slivers: [ |
||||||
|
SliverToBoxAdapter(child: SafeArea(child: Container(), left: false, bottom: false, right: false,),), |
||||||
|
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, |
||||||
|
), |
||||||
|
), |
||||||
|
], |
||||||
|
), |
||||||
|
bottomNavigationBar: snapshot.hasData ? BottomNavigationBar( |
||||||
|
items: [ |
||||||
|
BottomNavigationBarItem( |
||||||
|
icon: Icon(Icons.arrow_downward), |
||||||
|
label: ViewStationPageState.arrivals, |
||||||
|
), |
||||||
|
BottomNavigationBarItem( |
||||||
|
icon: Icon(Icons.arrow_upward), |
||||||
|
label: ViewStationPageState.departures, |
||||||
|
), |
||||||
|
], |
||||||
|
currentIndex: tab.index, |
||||||
|
onTap: onTabChange, |
||||||
|
) : null, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget buildStationArrivalItem(BuildContext context, StationArrival item) { |
||||||
|
return ListTile( |
||||||
|
leading: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'), |
||||||
|
title: Text.rich( |
||||||
|
TextSpan( |
||||||
|
children: [ |
||||||
|
TextSpan( |
||||||
|
text: item.train.rank, |
||||||
|
style: TextStyle( |
||||||
|
color: item.train.rank.startsWith('IR') ? Color.fromARGB(255, 255, 0, 0) : null, |
||||||
|
), |
||||||
|
), |
||||||
|
TextSpan(text: ' '), |
||||||
|
TextSpan(text: item.train.number,), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
subtitle: Text.rich( |
||||||
|
TextSpan( |
||||||
|
children: [ |
||||||
|
TextSpan(text: ViewStationPageState.arrivesFrom), |
||||||
|
TextSpan(text: ' '), |
||||||
|
TextSpan(text: item.train.origin), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
onTap: () => onTrainTapped(item.train.number), |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Widget buildStationDepartureItem(BuildContext context, StationDeparture item) { |
||||||
|
return ListTile( |
||||||
|
leading: Text('${item.time.toLocal().hour.toString().padLeft(2, '0')}:${item.time.toLocal().minute.toString().padLeft(2, '0')}'), |
||||||
|
title: Text.rich( |
||||||
|
TextSpan( |
||||||
|
children: [ |
||||||
|
TextSpan( |
||||||
|
text: item.train.rank, |
||||||
|
style: TextStyle( |
||||||
|
color: item.train.rank.startsWith('IR') ? Color.fromARGB(255, 255, 0, 0) : null, |
||||||
|
), |
||||||
|
), |
||||||
|
TextSpan(text: ' '), |
||||||
|
TextSpan(text: item.train.number,), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
subtitle: Text.rich( |
||||||
|
TextSpan( |
||||||
|
children: [ |
||||||
|
TextSpan(text: ViewStationPageState.departsTo), |
||||||
|
TextSpan(text: ' '), |
||||||
|
TextSpan(text: item.train.destination), |
||||||
|
], |
||||||
|
), |
||||||
|
), |
||||||
|
onTap: () => onTrainTapped(item.train.number), |
||||||
|
); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue