Line data Source code
1 : import 'package:app_pym/core/constants/mobility.dart';
2 : import 'package:app_pym/core/utils/time_formatter.dart';
3 : import 'package:app_pym/core/utils/url_launcher_utils.dart';
4 : import 'package:app_pym/domain/entities/mobility/trip.dart';
5 : import 'package:app_pym/injection_container.dart';
6 : import 'package:app_pym/presentation/blocs/mobility/stop_details/stop_details_bloc.dart';
7 : import 'package:flutter/material.dart';
8 : import 'package:flutter_bloc/flutter_bloc.dart';
9 :
10 : class DetailsBottomSheet extends StatelessWidget {
11 : final List<Trip> trips;
12 : final bool isBus;
13 : final String markerId;
14 :
15 0 : const DetailsBottomSheet(
16 : this.trips, {
17 : @required this.isBus,
18 : @required this.markerId,
19 : Key key,
20 0 : }) : super(key: key);
21 :
22 0 : @override
23 : Widget build(BuildContext context) {
24 0 : return Container(
25 0 : height: MediaQuery.of(context).size.height / 2,
26 0 : child: BlocProvider<StopDetailsBloc>(
27 0 : create: (_) => sl<StopDetailsBloc>()
28 0 : ..add(StopDetailsEvent.show(
29 0 : id: markerId,
30 0 : trips: trips,
31 0 : isBus: isBus,
32 : )),
33 0 : child: BlocBuilder<StopDetailsBloc, StopDetailsState>(
34 0 : builder: (context, state) {
35 0 : if (state.isLoading) {
36 : return const Center(child: CircularProgressIndicator());
37 0 : } else if (state.isError) {
38 0 : return Center(child: Text(state.exception.toString()));
39 : } else {
40 0 : final IconData icon = isBus ? Icons.directions_bus : Icons.train;
41 0 : final String line = isBus
42 : ? MobilityConstants.busLine
43 : : MobilityConstants.trainLine;
44 0 : return Padding(
45 : padding: const EdgeInsets.all(10.0),
46 0 : child: Column(
47 : crossAxisAlignment: CrossAxisAlignment.start,
48 0 : children: [
49 0 : Row(
50 0 : children: [
51 0 : Icon(
52 : icon,
53 0 : size: Theme.of(context).textTheme.headline4.fontSize,
54 : ),
55 0 : Text(
56 : line,
57 0 : style: Theme.of(context).textTheme.headline4,
58 : ),
59 : const Expanded(child: Text("")),
60 0 : Card(
61 0 : color: Theme.of(context).primaryColor,
62 0 : shape: RoundedRectangleBorder(
63 0 : borderRadius: BorderRadius.circular(18.0)),
64 0 : child: InkWell(
65 0 : onTap: () => UrlLauncherUtils.launch(isBus
66 : ? "https://www.lepilote.com/fr/horaires-des-lignes/6/LineTimeTable/183-8-mai-1945-gare-routiere-sncf/1598/gare-routiere-sncf/1"
67 : : "https://www.ter.sncf.com/sud-provence-alpes-cote-d-azur/gares/87751420/Gardanne/pratique"),
68 : child: const Padding(
69 : padding: EdgeInsets.all(10.0),
70 : child: Text("Horaires complets",
71 : style: TextStyle(color: Colors.white)),
72 : ),
73 : ),
74 : ),
75 : ],
76 : ),
77 0 : Text(
78 0 : state.stop_name,
79 0 : style: Theme.of(context).textTheme.subtitle1,
80 : ),
81 0 : Row(
82 0 : children: [
83 0 : Icon(
84 : Icons.arrow_forward,
85 0 : color: Theme.of(context).textTheme.subtitle2.color,
86 : ),
87 0 : Text(
88 0 : "Destination " + state.last_stop,
89 0 : style: Theme.of(context).textTheme.subtitle2,
90 : ),
91 : ],
92 : ),
93 0 : Row(
94 0 : children: [
95 : const Icon(Icons.arrow_right),
96 0 : Text(
97 0 : "${state.arrivalTimes[0]} (${state.arrivalTimes[0].arrivalTimeToTimeLeft().format()})",
98 0 : style: Theme.of(context).textTheme.bodyText1,
99 : ),
100 : ],
101 : ),
102 0 : Text(
103 0 : "${state.arrivalTimes[1]} (${state.arrivalTimes[1].arrivalTimeToTimeLeft().format()})",
104 : ),
105 0 : Text(
106 0 : "${state.arrivalTimes[2]} (${state.arrivalTimes[2].arrivalTimeToTimeLeft().format()})",
107 : ),
108 : const Divider(),
109 0 : Expanded(
110 0 : child: ListView.builder(
111 0 : itemCount: state.trip.length,
112 0 : itemBuilder: (context, index) {
113 : final Color color =
114 0 : state.trip[index].stop.stop_name ==
115 0 : state.destination
116 : ? Colors.red
117 0 : : Theme.of(context).textTheme.bodyText1.color;
118 : IconData stopIcon;
119 0 : if (state.trip[index].stop.stop_name ==
120 0 : state.last_stop) {
121 : stopIcon = Icons.fiber_manual_record;
122 0 : } else if (state.trip[index].stop.stop_name ==
123 0 : state.stop_name) {
124 : stopIcon = Icons.my_location;
125 : } else {
126 : stopIcon = Icons.arrow_drop_down;
127 : }
128 0 : final TextStyle nameStyle = TextStyle(
129 : color: color,
130 : fontWeight: FontWeight.bold,
131 : );
132 0 : final TextStyle hourStyle = TextStyle(color: color);
133 :
134 0 : return Row(
135 0 : children: [
136 0 : Padding(
137 : padding: const EdgeInsets.only(left: 20.0),
138 0 : child: Icon(
139 : stopIcon,
140 : color: color,
141 : ),
142 : ),
143 0 : Expanded(
144 0 : child: Text(
145 0 : state.trip[index].stop.stop_name,
146 : style: nameStyle,
147 : ),
148 : ),
149 0 : Text(
150 0 : state.trip[index].arrival_time,
151 : style: hourStyle,
152 : textAlign: TextAlign.end,
153 : ),
154 : ],
155 : );
156 : },
157 : ),
158 : ),
159 : ],
160 : ),
161 : );
162 : }
163 : },
164 : ),
165 : ),
166 : );
167 : }
168 : }
169 :
170 : extension on String {
171 0 : Duration arrivalTimeToTimeLeft() {
172 0 : final DateTime now = DateTime.now();
173 0 : final Duration timeLeft = DateTime(
174 0 : now.year,
175 0 : now.month,
176 0 : now.day,
177 0 : int.parse(this.split(':')[0]),
178 0 : int.parse(this.split(':')[1]),
179 0 : int.parse(this.split(':')[2]),
180 0 : ).difference(now);
181 0 : if (timeLeft.isNegative) {
182 0 : return timeLeft + const Duration(hours: 24);
183 : }
184 : return timeLeft;
185 : }
186 : }
|