import 'package:bus_infotainment/backend/live_information.dart'; import 'package:bus_infotainment/remaster/dashboard.dart'; import 'package:bus_infotainment/tfl_datasets.dart'; import 'package:bus_infotainment/utils/OrdinanceSurveyUtils.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart' hide NavigationBar; import 'package:flutter/widgets.dart'; import 'package:geolocator/geolocator.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:vector_math/vector_math.dart' hide Colors; import '../backend/modules/tube_info.dart'; class SearchArc extends StatefulWidget { @override State createState() => _SearchArcState(); } class _SearchArcState extends State { TextEditingController searchController = TextEditingController(); @override Widget build(BuildContext context) { List nearbyRoutes = _getNearbyRoutes(context); List pastRoutes = _getPastRoutes(context); List routeCards = []; if (searchController.text.isNotEmpty) { // Detect if the search query is a route number // Examples of route numbers: 1, A1, 1A, A1A ... // If it isnt a route number, it is a stop name // Examples of stop names: "Euston Station", "Baker Street", "Kings Cross" bool containsNumber = RegExp(r'\d').hasMatch(searchController.text); List searchResults = []; // Loop through all bus routes for (BusRoute route in LiveInformation().busSequences.routes.values) { if (containsNumber) { if (route.routeNumber.contains(searchController.text) && !searchResults.contains(route)) { routeCards.add(_getRouteCard(context, route)); searchResults.add(route); } } else { for (BusRouteVariant variant in route.routeVariants.values) { for (BusRouteStop stop in variant.busStops) { if (stop.formattedStopName.toLowerCase().contains( searchController.text.toLowerCase()) && !searchResults.contains(route)) { routeCards.add(_getRouteCard(context, route)); searchResults.add(route); break; } } } } } if (routeCards.isEmpty){ routeCards.add( Text("No results found", style: ShadTheme.of(context).textTheme.h3,) ); } } return Scaffold( body: Row( children: [ Expanded( child: Container( padding: EdgeInsets.fromLTRB(32, 16, 32, 0), height: MediaQuery.of(context).size.height, child: Column( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: [ ShadInput( placeholder: Text("Search for route or stop (Can be laggy)"), controller: searchController, onChanged: (value) { setState(() { // Update search results }); }, ), SizedBox(height: 8), if (routeCards.isNotEmpty) Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Search results", style: ShadTheme.of(context).textTheme.h3, ), SizedBox(height: 10), Expanded( child: GridView.extent( shrinkWrap: true, maxCrossAxisExtent: 100, scrollDirection: Axis.vertical, crossAxisSpacing: 8, mainAxisSpacing: 8, children: routeCards, ), ), ], ), ) else Expanded( child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Text( "Nearby routes", style: ShadTheme.of(context).textTheme.h3, ), SizedBox(height: 10), Expanded( child: GridView.extent( shrinkWrap: true, maxCrossAxisExtent: 100, scrollDirection: Axis.vertical, crossAxisSpacing: 8, mainAxisSpacing: 8, children: _getNearbyRoutes(context, multiMode: true), ), ), ], ), ) ], ), ), ], ), ), ), Container( width: 2, color: Colors.grey.shade300, ), RotatedBox( quarterTurns: 3, child: NavigationBar(), ) ], ), ); } } List _getNearbyRoutes(context, {bool multiMode = false}) { print("Getting nearby routes"); LiveInformation liveInformation = LiveInformation(); BusSequences busSequences = liveInformation.busSequences; List nearbyRoutes = []; Position? currentLocation = liveInformation.trackerModule.position; Vector2 currentVector = Vector2(0, 0); if (currentLocation == null && !kDebugMode) { return []; } else if (currentLocation != null){ currentVector = OSGrid.toNorthingEasting(currentLocation!.latitude, currentLocation.longitude); } if (kDebugMode) { currentVector = OSGrid.toNorthingEasting(51.583781262560926, -0.020359583104595073); } for (BusRoute route in busSequences.routes.values) { for (BusRouteVariant variant in route.routeVariants.values) { for (BusRouteStop stop in variant.busStops) { Vector2 stopVector = Vector2(stop.easting.toDouble(), stop.northing.toDouble()); double distance = currentVector.distanceTo(stopVector); if (distance < 1000) { nearbyRoutes.add(route); break; } } if (nearbyRoutes.contains(route)) { break; } } if (nearbyRoutes.contains(route)) { continue; } } List routeCards = []; for (BusRoute route in nearbyRoutes) { routeCards.add(_getRouteCard(context, route, multiMode: multiMode)); } return routeCards; } Widget _getRouteCard(context, BusRoute route, {bool multiMode = false}) { String rr = ""; if (route.routeNumber.toLowerCase().startsWith("ul")) { rr = "Rail replacement"; TubeLine? line = LiveInformation().tubeStations.getClosestLine(route.routeVariants.values.first); rr = line?.name ?? rr; if (!["London Overground", "DLR", "Rail replacement", "Elizabeth Line"].contains(rr)) { rr += " line"; } if (rr == "Hammersmith and City line") { rr = "Hammersmith & City"; } } return ElevatedButton( onPressed: () { showShadSheet( side: ShadSheetSide.right, context: context, builder: (context) { List variantWidgets = []; for (BusRouteVariant variant in route.routeVariants.values) { variantWidgets.add( ShadButton.outline( text: Container( width: 274, alignment: Alignment.centerLeft, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("${variant.busStops.first.formattedStopName} ->"), const SizedBox( height: 2, ), Text(variant.busStops.last.formattedStopName) ], ), ), width: double.infinity, height: 50, padding: const EdgeInsets.all(8), onPressed: () async { LiveInformation liveInformation = LiveInformation(); await liveInformation.setRouteVariant(variant); if (!multiMode) { Navigator.popAndPushNamed(context, "/enroute"); } else { Navigator.popAndPushNamed(context, "/multi/enroute"); } }, ) ); variantWidgets.add(const SizedBox( height: 4, )); } return ShadSheet( title: Text("Route ${route.routeNumber} - Variants"), content: Container( width: 300, height: MediaQuery.of(context).size.height - 52, child: Scrollbar( thumbVisibility: true, child: ListView( shrinkWrap: true, children: [ ...variantWidgets ], ), ), ), padding: const EdgeInsets.all(8), ); } ); }, child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( "Route \n ${route.routeNumber}", style: ShadTheme.of(context).textTheme.h4.copyWith( height: 1.1 ), textAlign: TextAlign.center, ), if (route.routeNumber.toLowerCase().startsWith("ul")) Text(rr, style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w400, height: 1), textAlign: TextAlign.center,) ], ), style: ElevatedButton.styleFrom( padding: EdgeInsets.all(10), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), ), );; } List _getPastRoutes(context) { return []; }