1
0

flightslist_page.dart 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. // ignore: unused_import
  2. import 'package:collection/collection.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:flutter_riverpod/flutter_riverpod.dart';
  5. import 'package:gap/gap.dart';
  6. import 'package:go_router/go_router.dart';
  7. import 'package:google_fonts/google_fonts.dart';
  8. import 'package:hive_flutter/hive_flutter.dart';
  9. import 'package:jiffy/jiffy.dart';
  10. import 'package:super_sliver_list/super_sliver_list.dart';
  11. import 'package:tp5/core/basic_page.dart';
  12. import 'package:tp5/core/utils.dart';
  13. import 'package:tp5/csv/data.dart';
  14. import 'package:tp5/flightslist/w_flt.dart';
  15. import 'package:tp5/fltinfo/view/fltinfo_page.dart';
  16. import 'package:tp5/roster/widgets/w_day.dart';
  17. class FlightslistPage extends ConsumerStatefulWidget {
  18. const FlightslistPage({super.key});
  19. @override
  20. ConsumerState<ConsumerStatefulWidget> createState() =>
  21. _FlightslistPageState();
  22. }
  23. class _FlightslistPageState extends ConsumerState<FlightslistPage> {
  24. final _listController = ListController();
  25. final _scrollController = ScrollController();
  26. void scrolltonow() {
  27. final jumpindex = _aclegs.indexWhere((e) => e.jdep!.isSameOrAfter(_now));
  28. if (jumpindex >= 0) {
  29. jumpToItem(jumpindex);
  30. }
  31. }
  32. void jumpToItem(int index) {
  33. try {
  34. if (_listController.isAttached && _scrollController.hasClients) {
  35. _listController.jumpToItem(
  36. //duration: (d) => const Duration(milliseconds: 700),
  37. index: index,
  38. scrollController: _scrollController,
  39. alignment: 0.75,
  40. //curve: (double estimatedDistance) => Curves.decelerate,
  41. );
  42. }
  43. } catch (e) {
  44. print("flistlistpage: jumptoitemscroll controller");
  45. }
  46. }
  47. bool showAllLegs = false;
  48. bool showInflightOnly = false;
  49. // bool _isfirstInDay(Acleg leg) =>
  50. // leg == _aclegs.firstWhereOrNull((e) => (e.jdep?.yMEd == leg.jdep?.yMEd));
  51. Widget get _acLegsWidget => Container(
  52. child: _aclegs.isEmpty
  53. ? const Text("No leg found !")
  54. : SuperListView.builder(
  55. delayPopulatingCacheArea: true,
  56. listController: _listController,
  57. controller: _scrollController,
  58. itemCount: _aclegs.length,
  59. itemBuilder: (context, index) {
  60. final leg = _aclegs.elementAt(index);
  61. return Column(
  62. children: [
  63. // if (_isfirstInDay(leg))
  64. if (index == 0 ||
  65. leg.jdep?.yMEd !=
  66. _aclegs.elementAt(index - 1).jdep?.yMEd)
  67. WDay(
  68. date: leg.jdep!,
  69. highlight: _now.yMEd == leg.jdep?.yMEd),
  70. InkWell(
  71. onTap: () => context.push("/fltinfo",
  72. extra: FltinfoParams(
  73. al: leg.FN_CARRIER,
  74. fnum: leg.FN_NUMBER,
  75. jdep: leg.jdep,
  76. jdes: leg.jarr,
  77. dep: leg.DEP_AP_ACTUAL,
  78. des: leg.ARR_AP_SCHED,
  79. )),
  80. child: WFlt(acleg: leg)),
  81. ],
  82. );
  83. },
  84. ));
  85. final bottomnavstyle = ElevatedButton.styleFrom(
  86. shape: RoundedRectangleBorder(
  87. borderRadius: BorderRadius.circular(5.0),
  88. ),
  89. backgroundColor:
  90. const Color.fromARGB(255, 0, 36, 53) //elevated btton background color
  91. );
  92. TextEditingController ctrldep = TextEditingController();
  93. TextEditingController ctrldes = TextEditingController();
  94. TextEditingController ctrlairline = TextEditingController();
  95. TextEditingController ctrlfnum = TextEditingController();
  96. Widget _flightFilter(BuildContext context) {
  97. return Container(
  98. padding: const EdgeInsets.all(20),
  99. child: Column(mainAxisSize: MainAxisSize.min, children: [
  100. Row(
  101. children: [
  102. Expanded(
  103. child: TextField(
  104. decoration:
  105. const InputDecoration(labelText: "Departure Airport"),
  106. maxLength: 3, // Set maximum length
  107. controller: ctrldep,
  108. ),
  109. ),
  110. const SizedBox(width: 10),
  111. Expanded(
  112. child: TextField(
  113. decoration:
  114. const InputDecoration(labelText: "Arrival Airport"),
  115. maxLength: 3, // Set maximum length
  116. controller: ctrldes,
  117. ),
  118. ),
  119. ],
  120. ),
  121. const SizedBox(height: 15),
  122. Row(
  123. children: [
  124. Expanded(
  125. child: TextField(
  126. controller: ctrlairline,
  127. decoration:
  128. const InputDecoration(labelText: "Airline IATA code"),
  129. maxLength: 2, // Set maximum length
  130. ),
  131. ),
  132. const SizedBox(width: 10),
  133. Expanded(
  134. child: TextField(
  135. controller: ctrlfnum,
  136. decoration: const InputDecoration(labelText: "Flight number"),
  137. maxLength: 6, // Set maximum length
  138. ),
  139. ),
  140. ],
  141. ),
  142. const SizedBox(height: 15),
  143. const SizedBox(height: 35),
  144. SizedBox(
  145. width: 360,
  146. child: ElevatedButton(
  147. onPressed: () async => Navigator.pop(context, {
  148. "dep": ctrldep.text.toUpperCase(),
  149. "des": ctrldes.text.toUpperCase(),
  150. "al": ctrlairline.text.toUpperCase(),
  151. "fnum": ctrlfnum.text.toUpperCase()
  152. }), // Close the bottom sheet
  153. style: ElevatedButton.styleFrom(
  154. backgroundColor: Colors.green, // Set button color to green
  155. shape: RoundedRectangleBorder(
  156. borderRadius:
  157. BorderRadius.circular(15), // Add rounded corners
  158. ),
  159. padding:
  160. const EdgeInsets.symmetric(horizontal: 20, vertical: 15),
  161. ),
  162. child: const Text(
  163. 'Flight filter',
  164. style: TextStyle(fontSize: 18, color: Colors.white),
  165. ),
  166. ),
  167. ),
  168. ]));
  169. }
  170. Map<String, String> flightFilter = {
  171. "dep": "",
  172. "des": "",
  173. "al": "",
  174. "fnum": ""
  175. };
  176. void _loadSearchFlights() {
  177. showInflightOnly = Hive.box("settings")
  178. .get("flightslist.filter.inflightonly", defaultValue: false);
  179. final filter = Hive.box("settings")
  180. .get("flightslist.filter.flights", defaultValue: <Map>{});
  181. // print("flightslist: loadsearchflights: saved: $filter");
  182. if (filter is Map) {
  183. for (var e in filter.keys) {
  184. flightFilter[e] = filter[e] ?? "";
  185. }
  186. }
  187. }
  188. void _searchFlights(xcontext) async {
  189. ctrldep.text = flightFilter["dep"] ?? "";
  190. ctrldes.text = flightFilter["des"] ?? "";
  191. ctrlairline.text = flightFilter["al"] ?? "";
  192. ctrlfnum.text = flightFilter["fnum"] ?? "";
  193. final res = await showModalBottomSheet(
  194. context: context, builder: (context) => _flightFilter(xcontext));
  195. if (res is Map<String, String>) {
  196. Hive.box("settings").put("flightslist.filter.flights", (res));
  197. setState(() {
  198. flightFilter = res;
  199. });
  200. }
  201. }
  202. @override
  203. void initState() {
  204. _loadSearchFlights();
  205. Future.delayed(const Duration(milliseconds: 700)).then((x) {
  206. scrolltonow();
  207. });
  208. super.initState();
  209. }
  210. List<Acleg> _allAclegs = [];
  211. List<Acleg> get _aclegs => _allAclegs
  212. .where((leg) =>
  213. (!showInflightOnly ||
  214. (showInflightOnly &&
  215. (leg.flt_status == "Inflight" ||
  216. leg.flt_status == "Taxiout"))) &&
  217. ((flightFilter["al"] == "" || leg.FN_CARRIER == flightFilter["al"]) &&
  218. (flightFilter["fnum"] == "" ||
  219. leg.FN_NUMBER == flightFilter["fnum"]) &&
  220. (flightFilter["dep"] == "" ||
  221. leg.DEP_AP_ACTUAL == flightFilter["dep"] ||
  222. leg.DEP_SCHED_DT == flightFilter["dep"]) &&
  223. (flightFilter["des"] == "" ||
  224. leg.ARR_AP_ACTUAL == flightFilter["des"] ||
  225. leg.ARR_AP_SCHED == flightFilter["des"])))
  226. .toList();
  227. Jiffy _now = Jiffy.now();
  228. @override
  229. Widget build(BuildContext context) {
  230. final data = ref.watch(dataProvider);
  231. _allAclegs = data.acleg;
  232. _now = ref.watch(clockProvider);
  233. return BasicPage(
  234. appBar: AppBar(
  235. title: const Text('Flights List'),
  236. ),
  237. body: Center(
  238. child: Column(
  239. children: [
  240. Expanded(child: _acLegsWidget),
  241. Row(
  242. mainAxisAlignment: MainAxisAlignment.end,
  243. children: [
  244. data.aclegupdate != null
  245. ? Text("data retrieved ${data.aclegupdate?.from(_now)}",
  246. style: GoogleFonts.robotoMono(
  247. fontSize: 10, fontWeight: FontWeight.w300))
  248. : Text("no data found",
  249. style: GoogleFonts.robotoMono(
  250. fontSize: 10, fontWeight: FontWeight.w300))
  251. ],
  252. )
  253. ],
  254. ),
  255. ),
  256. actions: [],
  257. bottomNavigationBar: Container(
  258. padding: const EdgeInsets.all(8),
  259. // color: Colors.black,
  260. decoration: BoxDecoration(
  261. gradient: LinearGradient(
  262. begin: Alignment.topCenter,
  263. end: Alignment.bottomCenter,
  264. colors: [
  265. Colors.grey[700]!,
  266. Colors.black,
  267. ],
  268. )),
  269. child: Row(
  270. mainAxisAlignment: MainAxisAlignment.spaceAround,
  271. children: [
  272. ElevatedButton.icon(
  273. onPressed: () async {
  274. showInflightOnly = !showInflightOnly;
  275. setState(() {});
  276. Future.delayed(Duration(milliseconds: 200)).then((x) {
  277. scrolltonow();
  278. });
  279. await Hive.box("settings")
  280. .put("flightslist.filter.inflightonly", showInflightOnly);
  281. },
  282. icon: showInflightOnly
  283. ? Icon(Icons.flight_takeoff)
  284. : Icon(Icons.all_out),
  285. label: showInflightOnly
  286. ? Text("Inflight\nonly")
  287. : Text("All flight\nstatus"),
  288. style: bottomnavstyle,
  289. ),
  290. const Gap(10),
  291. Row(
  292. children: [
  293. ElevatedButton.icon(
  294. onPressed: () {
  295. _searchFlights(context);
  296. },
  297. icon: const Icon(Icons.manage_search),
  298. label: const Text("Filter\nFlights"),
  299. style: bottomnavstyle,
  300. ),
  301. const Gap(10),
  302. Column(
  303. mainAxisSize: MainAxisSize.min,
  304. children: flightFilter.keys
  305. .where((e) => flightFilter[e] != "")
  306. .map((e) => Text(
  307. "$e: ${flightFilter[e]}",
  308. style: const TextStyle(
  309. fontSize: 12, color: Colors.yellow),
  310. ))
  311. .toList()),
  312. const Gap(10),
  313. if (flightFilter.values.any((e) => e != ""))
  314. Badge(
  315. label: Text(flightFilter.values
  316. .where((e) => e != "")
  317. .length
  318. .toString()),
  319. child: IconButton.outlined(
  320. onPressed: () {
  321. for (var k in flightFilter.keys) {
  322. flightFilter[k] = "";
  323. }
  324. Hive.box("settings")
  325. .delete("flightslist.filter.flights");
  326. setState(() {});
  327. },
  328. icon:
  329. const Icon(Icons.filter_alt_off_rounded, size: 18)),
  330. )
  331. ],
  332. ),
  333. ],
  334. ),
  335. ),
  336. );
  337. }
  338. }