shtuff
This commit is contained in:
BIN
assets/audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3
Normal file
BIN
assets/audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/BAKERLOO.mp3
Normal file
BIN
assets/audio/rail_replacement/BAKERLOO.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/CENTRAL.mp3
Normal file
BIN
assets/audio/rail_replacement/CENTRAL.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/CIRCLE.mp3
Normal file
BIN
assets/audio/rail_replacement/CIRCLE.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/DISTRICT.mp3
Normal file
BIN
assets/audio/rail_replacement/DISTRICT.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/DLR.mp3
Normal file
BIN
assets/audio/rail_replacement/DLR.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/HAMMERSMITH_N_CITY.mp3
Normal file
BIN
assets/audio/rail_replacement/HAMMERSMITH_N_CITY.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/JUBILEE.mp3
Normal file
BIN
assets/audio/rail_replacement/JUBILEE.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/LONDON_OVERGROUND.mp3
Normal file
BIN
assets/audio/rail_replacement/LONDON_OVERGROUND.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/METROPOLITAN.mp3
Normal file
BIN
assets/audio/rail_replacement/METROPOLITAN.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/NORTHERN.mp3
Normal file
BIN
assets/audio/rail_replacement/NORTHERN.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/PICCADILLY.mp3
Normal file
BIN
assets/audio/rail_replacement/PICCADILLY.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/TFL_RAIL.mp3
Normal file
BIN
assets/audio/rail_replacement/TFL_RAIL.mp3
Normal file
Binary file not shown.
BIN
assets/audio/rail_replacement/VICTORIA.mp3
Normal file
BIN
assets/audio/rail_replacement/VICTORIA.mp3
Normal file
Binary file not shown.
3929
assets/datasets/tube_stations.json
Normal file
3929
assets/datasets/tube_stations.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,7 @@ import 'package:bus_infotainment/backend/modules/announcement.dart';
|
||||
import 'package:bus_infotainment/backend/modules/commands.dart';
|
||||
import 'package:bus_infotainment/backend/modules/synced_time.dart';
|
||||
import 'package:bus_infotainment/backend/modules/tracker.dart';
|
||||
import 'package:bus_infotainment/backend/modules/tube_info.dart';
|
||||
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||
import 'package:bus_infotainment/utils/delegates.dart';
|
||||
@@ -57,6 +58,12 @@ class LiveInformation {
|
||||
print("Failed to load bus sequences from TFL. Using local copy.");
|
||||
}
|
||||
|
||||
// Load tube stations
|
||||
print("Loading tube stations from assets");
|
||||
tubeStations = TubeStations.fromJson(json.decode(await rootBundle.loadString("assets/datasets/tube_stations.json")));
|
||||
print("Loaded tube stations from assets");
|
||||
|
||||
|
||||
String sessionID = "test";
|
||||
|
||||
commandModule = CommandModule(sessionID);
|
||||
@@ -87,6 +94,7 @@ class LiveInformation {
|
||||
late AnnouncementModule announcementModule;
|
||||
late SyncedTimeModule syncedTimeModule;
|
||||
late TrackerModule trackerModule;
|
||||
late TubeStations tubeStations;
|
||||
|
||||
// Important variables
|
||||
BusRouteVariant? _currentRouteVariant;
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:bus_infotainment/audio_cache.dart';
|
||||
import 'package:bus_infotainment/backend/live_information.dart';
|
||||
import 'package:bus_infotainment/backend/modules/tube_info.dart';
|
||||
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||
import 'package:bus_infotainment/utils/delegates.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'info_module.dart';
|
||||
|
||||
@@ -33,12 +36,26 @@ class AnnouncementModule extends InfoModule {
|
||||
return _bundleBytes!;
|
||||
} else {
|
||||
|
||||
if (kIsWeb) {
|
||||
throw Exception("Cannot load bundle bytes on web");
|
||||
// Try to load them from shared preferences
|
||||
try {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
String fileLocation = prefs.getString("AnnouncementsFileLocation")!;
|
||||
|
||||
File file = File(fileLocation);
|
||||
setBundleBytes(file.readAsBytesSync());
|
||||
return _bundleBytes!;
|
||||
} catch (e) {
|
||||
throw Exception("Loading announcements from assets has been deprecated.");
|
||||
}
|
||||
|
||||
final bytes = await rootBundle.load(_bundleLocation);
|
||||
return bytes.buffer.asUint8List();
|
||||
|
||||
|
||||
// if (kIsWeb) {
|
||||
// throw Exception("Cannot load bundle bytes on web");
|
||||
// }
|
||||
//
|
||||
// final bytes = await rootBundle.load(_bundleLocation);
|
||||
// return bytes.buffer.asUint8List();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -274,11 +291,21 @@ class AnnouncementModule extends InfoModule {
|
||||
|
||||
String audioRoute = "R_${routeVariant.busRoute.routeNumber}_001.mp3";
|
||||
print("Checkpoint 5");
|
||||
await announcementCache.loadAnnouncementsFromBytes(await getBundleBytes(), [audioRoute, "R_RAIL_REPLACEMENT_SERVICE_001.mp3"]);
|
||||
await announcementCache.loadAnnouncementsFromBytes(await getBundleBytes(), [audioRoute]);
|
||||
print("Checkpoint 6");
|
||||
AudioWrapperSource sourceRoute = !routeNumber.toLowerCase().startsWith("ul") ?
|
||||
AudioWrapperByteSource(announcementCache[audioRoute]!) :
|
||||
AudioWrapperByteSource(announcementCache["R_RAIL_REPLACEMENT_SERVICE_001.mp3"]!);
|
||||
// AudioWrapperByteSource(announcementCache["R_RAIL_REPLACEMENT_SERVICE_001.mp3"]!);
|
||||
AudioWrapperAssetSource("audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3");
|
||||
|
||||
if (routeNumber.toLowerCase().startsWith("ul")) {
|
||||
|
||||
TubeLine? closestLine = liveInformation.tubeStations.getClosestLine(routeVariant);
|
||||
|
||||
sourceRoute = closestLine?.getAudio() ?? sourceRoute;
|
||||
routeNumber = closestLine?.getShortName() ?? routeNumber;
|
||||
}
|
||||
|
||||
print("Checkpoint 6.1");
|
||||
AudioWrapperSource sourceDestination = AudioWrapperByteSource(await routeVariant.destination!.getAudioBytes());
|
||||
print("Checkpoint 7");
|
||||
|
||||
@@ -90,7 +90,9 @@ class TrackerModule extends InfoModule {
|
||||
|
||||
int stopIndex = liveInformation.getRouteVariant()!.busStops.indexOf(closestStop);
|
||||
|
||||
closestStop = liveInformation.getRouteVariant()!.busStops[stopIndex + 1];
|
||||
int maxStops = liveInformation.getRouteVariant()!.busStops.length;
|
||||
|
||||
closestStop = liveInformation.getRouteVariant()!.busStops[min(stopIndex + 1, maxStops)];
|
||||
|
||||
print("Closest stop is now: ${closestStop.formattedStopName}");
|
||||
} else {
|
||||
|
||||
133
lib/backend/modules/tube_info.dart
Normal file
133
lib/backend/modules/tube_info.dart
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
|
||||
import 'package:bus_infotainment/utils/OrdinanceSurveyUtils.dart';
|
||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||
import 'package:vector_math/vector_math.dart';
|
||||
|
||||
import '../../tfl_datasets.dart';
|
||||
|
||||
class TubeStations {
|
||||
|
||||
List<TubeStation> stations = [];
|
||||
List<TubeLine> lines = [];
|
||||
|
||||
TubeStations.fromJson(Map<String, dynamic> json) {
|
||||
/*
|
||||
Example:
|
||||
"Acton Town": {
|
||||
"lines": [
|
||||
"District",
|
||||
"Piccadilly"
|
||||
],
|
||||
"location": {
|
||||
"lat": 51.5029984,
|
||||
"lng": -0.2803455
|
||||
}
|
||||
},
|
||||
*/
|
||||
|
||||
for (String stationName in json.keys) {
|
||||
TubeStation station = TubeStation();
|
||||
station.name = stationName;
|
||||
|
||||
Map<String, dynamic> stationData = json[stationName];
|
||||
|
||||
station.latitude = stationData["location"]["lat"];
|
||||
station.longitude = stationData["location"]["lng"];
|
||||
|
||||
for (String lineName in stationData["lines"]) {
|
||||
|
||||
// check if the line already exists
|
||||
TubeLine line = lines.firstWhere((element) => element.name == lineName, orElse: () => TubeLine()..name = lineName);
|
||||
|
||||
if (!lines.contains(line)) {
|
||||
lines.add(line);
|
||||
}
|
||||
|
||||
station.lines.add(line);
|
||||
}
|
||||
|
||||
stations.add(station);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Get the london underground line that is a close match to a given bus route variant
|
||||
TubeLine? getClosestLine(BusRouteVariant variant) {
|
||||
|
||||
Map<TubeLine, int> lineMatches = {};
|
||||
|
||||
for (TubeLine line in lines) {
|
||||
lineMatches[line] = 0;
|
||||
}
|
||||
|
||||
for (BusRouteStop stop in variant.busStops) {
|
||||
for (TubeStation station in stations) {
|
||||
|
||||
// get the distance between the bus stop and the tube station
|
||||
double distance = Vector2(stop.easting.toDouble(), stop.northing.toDouble()).distanceTo(OSGrid.toNorthingEasting(station.latitude, station.longitude));
|
||||
|
||||
// if the distance is less than 100m, then we can assume that the bus stop is near the tube station
|
||||
if (distance < 200) {
|
||||
for (TubeLine line in station.lines) {
|
||||
lineMatches[line] = lineMatches[line]! + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TubeLine? closestLine;
|
||||
int closestMatch = 0;
|
||||
for (TubeLine line in lineMatches.keys) {
|
||||
if (lineMatches[line]! > closestMatch) {
|
||||
closestLine = line;
|
||||
closestMatch = lineMatches[line]!;
|
||||
}
|
||||
}
|
||||
|
||||
return closestLine;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TubeStation {
|
||||
|
||||
late final String name;
|
||||
|
||||
late final double latitude;
|
||||
late final double longitude;
|
||||
|
||||
List<TubeLine> lines = [];
|
||||
|
||||
}
|
||||
|
||||
class TubeLine {
|
||||
|
||||
late final String name;
|
||||
|
||||
AudioWrapperSource? getAudio() {
|
||||
|
||||
String lineName = name.toLowerCase().replaceAll(" ", "_");
|
||||
lineName = lineName.replaceAll("and", "N");
|
||||
lineName = lineName.toUpperCase();
|
||||
|
||||
return AudioWrapperAssetSource("audio/rail_replacement/$lineName.mp3");
|
||||
}
|
||||
|
||||
// Get short name
|
||||
// example: "District" -> "DIS", "London Overground" -> "LO", "TfL Rail" -> "TFL", "Waterloo and City" -> "W&C"
|
||||
String getShortName() {
|
||||
String shortName = name;
|
||||
|
||||
List<String> words = shortName.split(" ");
|
||||
shortName = words.map((e) => e[0]).join("");
|
||||
|
||||
if (shortName.length < 3) {
|
||||
shortName = name.substring(0, 3).toUpperCase();
|
||||
}
|
||||
|
||||
return shortName;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -88,7 +88,6 @@ class MyApp extends StatelessWidget {
|
||||
// '/': (context) => TfL_Dataset_Test(),
|
||||
'/': (context) => remaster.InitialStartup(),
|
||||
'/application': (context) => TfL_Dataset_Test(),
|
||||
'/dashboard': (context) => Dashboard(),
|
||||
|
||||
},
|
||||
);
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:flutter/widgets.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class InitialStartup extends StatefulWidget {
|
||||
|
||||
@@ -324,6 +325,8 @@ class _page3 extends InitialStartupPage {
|
||||
|
||||
class _page3State extends State<_page3> {
|
||||
|
||||
bool _loadingAudio = false;
|
||||
|
||||
Future<bool> _announcementsUploaded() async {
|
||||
try {
|
||||
Uint8List bytes = await LiveInformation().announcementModule.getBundleBytes();
|
||||
@@ -393,6 +396,7 @@ class _page3State extends State<_page3> {
|
||||
height: 4,
|
||||
),
|
||||
|
||||
if (!_loadingAudio)
|
||||
FutureBuilder(
|
||||
future: _announcementsUploaded(),
|
||||
builder: (context, val) {
|
||||
@@ -412,6 +416,10 @@ class _page3State extends State<_page3> {
|
||||
text: Text(text),
|
||||
onPressed: () async {
|
||||
|
||||
setState(() {
|
||||
_loadingAudio = true;
|
||||
});
|
||||
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.custom,
|
||||
allowedExtensions: [
|
||||
@@ -419,6 +427,8 @@ class _page3State extends State<_page3> {
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
|
||||
if (result != null) {
|
||||
late Uint8List bytes;
|
||||
|
||||
@@ -434,13 +444,25 @@ class _page3State extends State<_page3> {
|
||||
|
||||
AnnouncementCache cache = LiveInformation().announcementModule.announcementCache;
|
||||
|
||||
if (!kIsWeb) {
|
||||
|
||||
// Use shared preferences to store the file location
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
prefs.setString("AnnouncementsFileLocation", result.files.single.path!);
|
||||
}
|
||||
|
||||
// load a random announcement to ensure that the file is usable
|
||||
await cache.loadAnnouncementsFromBytes(bytes, ["S_WALTHAMSTOW_CENTRAL_001.mp3"]);
|
||||
|
||||
setState(() {
|
||||
|
||||
_loadingAudio = false;
|
||||
});
|
||||
|
||||
} else {
|
||||
setState(() {
|
||||
_loadingAudio = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -450,11 +472,62 @@ class _page3State extends State<_page3> {
|
||||
);
|
||||
},
|
||||
)
|
||||
else
|
||||
CircularProgressIndicator(),
|
||||
|
||||
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
|
||||
FutureBuilder(
|
||||
future: _announcementsUploaded(),
|
||||
builder: (context, val) {
|
||||
bool isEnabled = true;
|
||||
String text = "Continue";
|
||||
Color color = Colors.white;
|
||||
|
||||
if (val.hasData) {
|
||||
isEnabled = val.data!;
|
||||
}
|
||||
if (!isEnabled) {
|
||||
text = "Complete the prerequisites to continue";
|
||||
}
|
||||
|
||||
return ShadButton(
|
||||
text: Text(text),
|
||||
onPressed: () async {
|
||||
|
||||
showShadDialog(
|
||||
context: context,
|
||||
builder: (context) => ShadDialog.alert(
|
||||
title: Text("You're all setup"),
|
||||
description: Text("You can now continue to the application. \nTry not to annoy anyone on the bus! ;)"),
|
||||
actions: [
|
||||
ShadButton(
|
||||
text: Text("Continue to application"),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, "/");
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
barrierDismissible: false
|
||||
);
|
||||
|
||||
},
|
||||
enabled: isEnabled,
|
||||
backgroundColor: color,
|
||||
width: double.infinity,
|
||||
);
|
||||
},
|
||||
)
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
|
||||
import 'package:bus_infotainment/remaster/InitialStartup.dart';
|
||||
import 'package:bus_infotainment/remaster/dashboard.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
|
||||
@@ -17,7 +18,11 @@ class RemasteredApp extends StatelessWidget {
|
||||
),
|
||||
|
||||
routes: {
|
||||
'/': (context) => InitialStartup()
|
||||
'/setup': (context) => InitialStartup(),
|
||||
'/': (context) => HomePage_Re(),
|
||||
'/routes': (context) => RoutePage(),
|
||||
'/enroute': (context) => EnRoutePage(),
|
||||
|
||||
},
|
||||
|
||||
);
|
||||
|
||||
@@ -1,365 +1,510 @@
|
||||
|
||||
import 'package:bus_infotainment/backend/live_information.dart';
|
||||
import 'package:bus_infotainment/pages/components/ibus_display.dart';
|
||||
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||
import 'package:text_scroll/text_scroll.dart';
|
||||
|
||||
import '../backend/modules/tube_info.dart';
|
||||
|
||||
Color rgb(int r, int g, int b) {
|
||||
return Color.fromRGBO(r, g, b, 1);
|
||||
}
|
||||
|
||||
class Dashboard extends StatelessWidget {
|
||||
class HomePage_Re extends StatefulWidget {
|
||||
|
||||
@override
|
||||
State<HomePage_Re> createState() => _HomePage_ReState();
|
||||
}
|
||||
|
||||
class _HomePage_ReState extends State<HomePage_Re> {
|
||||
Future<bool> _shouldRedirectToSetup() async {
|
||||
|
||||
List<bool> perms = [];
|
||||
|
||||
perms.addAll([
|
||||
await Permission.manageExternalStorage.isGranted,
|
||||
await Permission.location.isGranted
|
||||
]);
|
||||
|
||||
bool shouldRedirectA = !perms.contains(false);
|
||||
bool shouldRedirectB = true;
|
||||
|
||||
try {
|
||||
Uint8List bytes = await LiveInformation().announcementModule.getBundleBytes();
|
||||
shouldRedirectB = true;
|
||||
} catch (e) {
|
||||
print("Failed to load bundle");
|
||||
shouldRedirectB = false;
|
||||
}
|
||||
|
||||
print("Should redirect to setup: ${shouldRedirectA || shouldRedirectB}");
|
||||
print("Permissions: $shouldRedirectA");
|
||||
print("Bundle: $shouldRedirectB");
|
||||
print("Permissions_indv: $perms");
|
||||
|
||||
return !shouldRedirectA || !shouldRedirectB;
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
|
||||
_shouldRedirectToSetup().then((value) {
|
||||
if (value) {
|
||||
Navigator.pushNamed(context, "/setup");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement build
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
|
||||
backgroundColor: Colors.grey.shade900,
|
||||
padding: EdgeInsets.all(16),
|
||||
|
||||
bottomNavigationBar: ((defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS)) ? null : BottomAppBar(
|
||||
alignment: Alignment.center,
|
||||
|
||||
color: Colors.grey.shade800,
|
||||
child: SizedBox(
|
||||
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.home),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, "/dashboard");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.search),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, "/search");
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.settings),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, "/settings");
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
width: double.infinity,
|
||||
|
||||
body: Column(
|
||||
children: [
|
||||
child: Column(
|
||||
|
||||
Container(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
margin: EdgeInsets.all(16),
|
||||
padding: EdgeInsets.all(8),
|
||||
children: [
|
||||
|
||||
decoration: BoxDecoration(
|
||||
Text(
|
||||
"Choose mode:",
|
||||
style: TextStyle(
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w600,
|
||||
)
|
||||
),
|
||||
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
|
||||
color: Colors.grey.shade800
|
||||
ShadCard(
|
||||
title: Text("Solo mode"),
|
||||
width: double.infinity,
|
||||
description: Text(
|
||||
"Choose this mode if you are only using this device. (No internet required)"
|
||||
),
|
||||
content: Column(
|
||||
|
||||
),
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
// height: 100,
|
||||
children: [
|
||||
|
||||
child: ibus_display()
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: Row(
|
||||
|
||||
children: [
|
||||
|
||||
if (false)
|
||||
if (!(defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS))
|
||||
NavigationRail(
|
||||
|
||||
selectedIndex: 0,
|
||||
|
||||
groupAlignment: 1,
|
||||
|
||||
destinations: [
|
||||
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.home),
|
||||
label: Text("Dashboard"),
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.search),
|
||||
label: Text("Routes")
|
||||
),
|
||||
|
||||
NavigationRailDestination(
|
||||
icon: Icon(Icons.settings),
|
||||
label: Text("Settings")
|
||||
ShadButton.secondary(
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, "/routes");
|
||||
},
|
||||
text: Text("Continue"),
|
||||
)
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: Container(
|
||||
SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
|
||||
child: Column(
|
||||
ShadCard(
|
||||
title: Text("Multi mode"),
|
||||
width: double.infinity,
|
||||
description: Text(
|
||||
"Choose this mode if you are using multiple devices. (Internet required)"
|
||||
),
|
||||
content: Column(
|
||||
|
||||
children: [
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
Container(
|
||||
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 16
|
||||
),
|
||||
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
|
||||
children: [
|
||||
|
||||
Expanded(
|
||||
child: Container(
|
||||
|
||||
decoration: BoxDecoration(
|
||||
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
|
||||
color: Colors.grey.shade800
|
||||
|
||||
),
|
||||
|
||||
padding: EdgeInsets.all(16),
|
||||
|
||||
child: IntrinsicWidth(
|
||||
child: Column(
|
||||
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
|
||||
children: [
|
||||
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
|
||||
children: [
|
||||
Text(
|
||||
"Bus Route:",
|
||||
style: GoogleFonts.interTight(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white
|
||||
)
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
|
||||
Text(
|
||||
"11",
|
||||
style: GoogleFonts.interTight(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.white
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Destination:",
|
||||
style: GoogleFonts.interTight(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white
|
||||
)
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
|
||||
Text(
|
||||
"Fullham Broadway",
|
||||
style: GoogleFonts.interTight(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.white
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
|
||||
Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Next Stop:",
|
||||
style: GoogleFonts.interTight(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white
|
||||
)
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
|
||||
Text(
|
||||
"St Thomas Hospital / County Hall",
|
||||
style: GoogleFonts.interTight(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.white
|
||||
),
|
||||
overflow: TextOverflow.fade,
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
Row(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Last Stop:",
|
||||
style: GoogleFonts.interTight(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white
|
||||
),
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
|
||||
Text(
|
||||
"Fullham Town Hall",
|
||||
style: GoogleFonts.interTight(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: Colors.white
|
||||
),
|
||||
overflow: TextOverflow.fade,
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
)
|
||||
|
||||
),
|
||||
),
|
||||
|
||||
// SizedBox(
|
||||
// width: 16,
|
||||
// ),
|
||||
//
|
||||
// Column(
|
||||
//
|
||||
// children: [
|
||||
//
|
||||
// SizedBox(
|
||||
//
|
||||
// width: 150,
|
||||
//
|
||||
// child: FloatingActionButton(
|
||||
// onPressed: () {
|
||||
//
|
||||
// },
|
||||
//
|
||||
// backgroundColor: Colors.red,
|
||||
//
|
||||
// child: Text(
|
||||
// "Bus Stopping",
|
||||
// style: GoogleFonts.interTight(
|
||||
// fontSize: 18,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.white
|
||||
// )
|
||||
//
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
//
|
||||
// SizedBox(
|
||||
// height: 16
|
||||
// ),
|
||||
//
|
||||
// SizedBox(
|
||||
//
|
||||
// width: 150,
|
||||
// height: 100,
|
||||
//
|
||||
// child: FloatingActionButton(
|
||||
// onPressed: () {
|
||||
//
|
||||
// },
|
||||
//
|
||||
// backgroundColor: Colors.grey.shade600,
|
||||
//
|
||||
// child: Text(
|
||||
// "Acknowledge Bus Stop",
|
||||
// style: GoogleFonts.interTight(
|
||||
// fontSize: 18,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// color: Colors.white
|
||||
// ),
|
||||
// textAlign: TextAlign.center,
|
||||
//
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
//
|
||||
// ],
|
||||
//
|
||||
// )
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
],
|
||||
children: [
|
||||
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
|
||||
),
|
||||
)
|
||||
ShadButton.secondary(
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, "/application");
|
||||
},
|
||||
text: Text("Continue"),
|
||||
)
|
||||
|
||||
],
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RoutePage extends StatelessWidget {
|
||||
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
|
||||
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
|
||||
padding: EdgeInsets.all(16),
|
||||
|
||||
alignment: Alignment.center,
|
||||
|
||||
child: SizedBox(
|
||||
|
||||
width: double.infinity,
|
||||
|
||||
child: Column(
|
||||
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
children: [
|
||||
|
||||
Text(
|
||||
"Routes",
|
||||
style: ShadTheme.of(context).textTheme.h1,
|
||||
),
|
||||
|
||||
Text(
|
||||
"Nearby routes",
|
||||
style: ShadTheme.of(context).textTheme.h4,
|
||||
),
|
||||
|
||||
ExpandableCarousel(
|
||||
options: CarouselOptions(
|
||||
|
||||
),
|
||||
items: [
|
||||
ShadCard(
|
||||
title: Text("Route 34"),
|
||||
content: ShadSelect(
|
||||
options: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text("Choose a variant"),
|
||||
)
|
||||
],
|
||||
selectedOptionBuilder: (BuildContext context, value) {
|
||||
return Text("Choose a variant");
|
||||
},
|
||||
),
|
||||
padding: EdgeInsets.all(10),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
Divider(),
|
||||
|
||||
RouteSearch()
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
)
|
||||
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
List<Widget> _getNearbyRoutes() {
|
||||
|
||||
final variants = [
|
||||
"Walthamstow Central to Barnet Church",
|
||||
"Walthamstow Central to Barnet Church",
|
||||
];
|
||||
|
||||
List<Widget> widgets = [];
|
||||
|
||||
for (int i = 0; i < variants.length; i++) {
|
||||
widgets.add(
|
||||
ShadOption(
|
||||
value: i,
|
||||
child: Text(variants[i]!),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return widgets;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class RouteSearch extends StatefulWidget {
|
||||
|
||||
@override
|
||||
State<RouteSearch> createState() => _RouteSearchState();
|
||||
}
|
||||
|
||||
class _RouteSearchState extends State<RouteSearch> {
|
||||
TextEditingController controller = TextEditingController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
List<Widget> routes = [];
|
||||
|
||||
for (BusRoute route in LiveInformation().busSequences.routes.values.toList()) {
|
||||
|
||||
if (controller.text.isNotEmpty && !route.routeNumber.toLowerCase().contains(controller.text.toLowerCase())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
routes.add(RouteCard(route: route));
|
||||
}
|
||||
|
||||
return Expanded(
|
||||
child: Column(
|
||||
|
||||
children: [
|
||||
|
||||
ShadInput(
|
||||
placeholder: Text("Search for a route..."),
|
||||
controller: controller,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
|
||||
});
|
||||
},
|
||||
),
|
||||
|
||||
SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
|
||||
Expanded(
|
||||
child: GridView.count(
|
||||
crossAxisCount: 3,
|
||||
children: [
|
||||
...routes
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RouteCard extends StatelessWidget {
|
||||
|
||||
BusRoute route;
|
||||
|
||||
RouteCard({required this.route});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement build
|
||||
|
||||
Map<String, Widget> variants = {};
|
||||
|
||||
for (BusRouteVariant variant in route.routeVariants.values) {
|
||||
String variantLabel = "${variant.busStops.first.formattedStopName} -> ${variant.busStops.last.formattedStopName}";
|
||||
|
||||
variants[variantLabel] = ShadOption(
|
||||
value: variant.routeVariant.toString(),
|
||||
child: Text(variantLabel),
|
||||
);
|
||||
}
|
||||
|
||||
return AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
child: ShadButton.secondary(
|
||||
text: Text(
|
||||
"Route \n ${route.routeNumber}",
|
||||
style: ShadTheme.of(context).textTheme.h3
|
||||
),
|
||||
padding: EdgeInsets.all(8),
|
||||
width: 105,
|
||||
height: 105,
|
||||
|
||||
onPressed: () {
|
||||
showShadSheet(
|
||||
side: ShadSheetSide.bottom,
|
||||
context: context,
|
||||
builder: (context) {
|
||||
|
||||
List<Widget> variantWidgets = [];
|
||||
|
||||
for (BusRouteVariant variant in route.routeVariants.values) {
|
||||
String variantLabel = "${variant.busStops.first.formattedStopName} -> ${variant.busStops.last.formattedStopName}";
|
||||
|
||||
variantWidgets.add(
|
||||
ShadButton.outline(
|
||||
text: SizedBox(
|
||||
width: 800-490,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text("${variant.busStops.first.formattedStopName} ->"),
|
||||
SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
Text(variant.busStops.last.formattedStopName)
|
||||
],
|
||||
),
|
||||
),
|
||||
width: double.infinity,
|
||||
height: 50,
|
||||
padding: EdgeInsets.all(8),
|
||||
onPressed: () async {
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
await liveInformation.setRouteVariant(variant);
|
||||
|
||||
liveInformation.announcementModule.queueAnnouncementByRouteVariant(routeVariant: variant);
|
||||
|
||||
Navigator.pushNamed(context, "/enroute");
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
variantWidgets.add(SizedBox(
|
||||
height: 4,
|
||||
));
|
||||
}
|
||||
|
||||
return ShadSheet(
|
||||
title: Text("Route ${route.routeNumber} - Variants"),
|
||||
|
||||
content: Container(
|
||||
width: 2000,
|
||||
alignment: Alignment.center,
|
||||
child: Column(
|
||||
children: [
|
||||
...variantWidgets
|
||||
],
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.all(8),
|
||||
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class EnRoutePage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement build
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
|
||||
children: [
|
||||
|
||||
Container(
|
||||
padding: EdgeInsets.all(8),
|
||||
child: ibus_display()
|
||||
),
|
||||
|
||||
Divider(
|
||||
height: 1,
|
||||
),
|
||||
|
||||
ShadButton(
|
||||
text: Text("Route scanner"),
|
||||
onPressed: () {
|
||||
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
|
||||
TubeLine? line = liveInformation.tubeStations.getClosestLine(liveInformation.getRouteVariant()!);
|
||||
|
||||
ShadToaster.of(context).show(
|
||||
ShadToast(
|
||||
title: Text("Closest line"),
|
||||
description: Text(line == null ? "No line found" : line.name),
|
||||
duration: Duration(seconds: 5),
|
||||
)
|
||||
);
|
||||
|
||||
},
|
||||
),
|
||||
|
||||
ShadButton(
|
||||
text: Text("dest"),
|
||||
onPressed: () {
|
||||
LiveInformation liveInformation = LiveInformation();
|
||||
liveInformation.announcementModule.queueAnnouncementByRouteVariant(routeVariant: liveInformation.getRouteVariant()!);
|
||||
},
|
||||
)
|
||||
|
||||
],
|
||||
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
ShadSelectFormField<String>(
|
||||
options: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text("Choose a variant"),
|
||||
),
|
||||
...variants.values
|
||||
],
|
||||
selectedOptionBuilder: (context, value) => value ==
|
||||
'none'
|
||||
? const Text('Select a verified email to display')
|
||||
: Text(variants.keys.toList()[int.parse(value)-1]!),
|
||||
placeholder: Text("Choose a variant"),
|
||||
|
||||
),
|
||||
*/
|
||||
@@ -98,6 +98,18 @@ class BusSequences extends InfoModule {
|
||||
|
||||
print("Loaded ${routes.length} routes");
|
||||
|
||||
// Remove any empty routes
|
||||
List<String> emptyRoutes = [];
|
||||
for (String routeNumber in routes.keys) {
|
||||
if (routes[routeNumber]!.routeVariants.isEmpty) {
|
||||
emptyRoutes.add(routeNumber);
|
||||
}
|
||||
}
|
||||
|
||||
for (String routeNumber in emptyRoutes) {
|
||||
routes.remove(routeNumber);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -225,6 +237,7 @@ class BusRouteStop {
|
||||
|
||||
// Replace space with underscore
|
||||
stopName = stopName.replaceAll(' ', '_');
|
||||
stopName = stopName.replaceAll('-', '_');
|
||||
|
||||
// convert to all caps
|
||||
stopName = stopName.toUpperCase();
|
||||
@@ -263,6 +276,7 @@ class BusDestination {
|
||||
|
||||
// Replace space with underscore
|
||||
name = name.replaceAll(' ', '_');
|
||||
name = name.replaceAll('-', '_');
|
||||
|
||||
// convert to all caps
|
||||
name = name.toUpperCase();
|
||||
|
||||
@@ -27,8 +27,8 @@ class NameBeautify {
|
||||
stopName = stopName.replaceAll(RegExp(r'\>.*\<'), '');
|
||||
|
||||
|
||||
// remove any special characters except & and /
|
||||
stopName = stopName.replaceAll(RegExp(r'[^a-zA-Z0-9&/ ]'), '');
|
||||
// remove any special characters except & and / and -
|
||||
stopName = stopName.replaceAll(RegExp(r'[^a-zA-Z0-9&\/\- ]'), '');
|
||||
|
||||
// remove any double spaces
|
||||
stopName = stopName.replaceAll(RegExp(r' '), ' ');
|
||||
@@ -41,12 +41,17 @@ class NameBeautify {
|
||||
stopName = stopName.replaceAll(RegExp(phrase, caseSensitive: false), Longify[phrase]!);
|
||||
}
|
||||
|
||||
// remove any spaces at the start or end of the string
|
||||
stopName = stopName.trim();
|
||||
stopName = stopName.replaceAll(' ', ' ');
|
||||
|
||||
stopName = stopName.toLowerCase();
|
||||
// Capitalify the first letter of each word
|
||||
try {
|
||||
stopName = stopName.split(' ').map((word) => word[0].toUpperCase() + word.substring(1)).join(' ');
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
print("Error capitalifying stop name: $stopName");
|
||||
}
|
||||
|
||||
return stopName;
|
||||
|
||||
|
||||
@@ -25,14 +25,14 @@ class AudioWrapper {
|
||||
|
||||
print("AudioWrapper mode: $mode");
|
||||
|
||||
mode = AudioWrapper_Mode.Mobile;
|
||||
mode = AudioWrapper_Mode.Web;
|
||||
}
|
||||
|
||||
justaudio.AudioSource _convertSource_JustAudio(AudioWrapperSource source){
|
||||
if (source is AudioWrapperByteSource){
|
||||
return _ByteSource(source.bytes);
|
||||
} else if (source is AudioWrapperAssetSource){
|
||||
return justaudio.AudioSource.asset(source.assetPath);
|
||||
return justaudio.AudioSource.asset("assets/" + source.assetPath);
|
||||
} else {
|
||||
throw Exception("Unknown source type");
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||
#include <rive_common/rive_plugin.h>
|
||||
#include <screen_retriever/screen_retriever_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
#include <window_manager/window_manager_plugin.h>
|
||||
@@ -16,6 +17,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
||||
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
||||
g_autoptr(FlPluginRegistrar) rive_common_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "RivePlugin");
|
||||
rive_plugin_register_with_registrar(rive_common_registrar);
|
||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
audioplayers_linux
|
||||
rive_common
|
||||
screen_retriever
|
||||
url_launcher_linux
|
||||
window_manager
|
||||
|
||||
84
pubspec.lock
84
pubspec.lock
@@ -5,18 +5,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: appwrite
|
||||
sha256: "335faac24642aaf66627c21ce26a5c64bcbc3911e624c61b9dfbe0eec7be1342"
|
||||
sha256: d95476adb1bbb72ae9eb31adffb9205b5cfe24abc4f483982eadceb396d433fb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.1"
|
||||
version: "12.0.3"
|
||||
archive:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: archive
|
||||
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
||||
sha256: "0763b45fa9294197a2885c8567927e2830ade852e5c896fd4ab7e0e348d0f373"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.10"
|
||||
version: "3.5.0"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -145,14 +145,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
cookie_jar:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -197,10 +189,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110"
|
||||
sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.1.2"
|
||||
version: "10.1.0"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -245,10 +237,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: b6283d7387310ad83bc4f3bc245b75d223a032ae6eba275afcd585de2b9a1476
|
||||
sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.1"
|
||||
version: "8.0.3"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -270,6 +262,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.0"
|
||||
flutter_carousel_widget:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_carousel_widget
|
||||
sha256: "37b9e55e4cafffe358152b016db24153e756152aa07c4214cfe6ee902cd69a01"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -437,14 +437,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.18.1"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
just_audio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -473,10 +465,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: just_audio_windows
|
||||
sha256: "7b8801f3987e98a2002cd23b5600b2daf162248ff1413266fb44c84448c1c0d3"
|
||||
sha256: "48ab2dec79cf6097550602fe07b1a644f341450e138dc8fdc23e42ce0ed2d928"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
version: "0.2.1"
|
||||
latlong2:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -573,14 +565,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
modular_ui:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: modular_ui
|
||||
sha256: c7bfa2d509f6b55f45833ea5a46c7b40030c2a3c8fb26f5211eab440235ee99f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.5"
|
||||
ntp:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -593,18 +577,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017"
|
||||
sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.0"
|
||||
version: "8.0.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
|
||||
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "3.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -741,14 +725,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
pointycastle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pointycastle
|
||||
sha256: "79fbafed02cfdbe85ef3fd06c7f4bc2cbcba0177e61b765264853d4253b21744"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.9.0"
|
||||
polylabel:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -769,18 +745,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rive
|
||||
sha256: ec44b6cf7341e21727c4b0e762f4ac82f9a45f7e52df3ebad2d1289a726fbaaf
|
||||
sha256: "95690a0fb4f6e195c53b217ab3cc0e0b0f443c670adbdee9d57d636a36b82b18"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.1"
|
||||
version: "0.13.2"
|
||||
rive_common:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: rive_common
|
||||
sha256: "0f070bc0e764c570abd8b34d744ef30d1292bd4051f2e0951bbda755875fce6a"
|
||||
sha256: "3eee68fcab3e0882090cea5a8cf7acea7967f469a34a2580322575603b094435"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3"
|
||||
version: "0.4.5"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -801,10 +777,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shadcn_ui
|
||||
sha256: "06c25555efbabe6d1d6acd6397bff4940e931d53ac3448e3a4f6a2287ab6798d"
|
||||
sha256: df9aedbd18bd60160c1c54717eef47fe4f90422f843070ccbc32786193803f7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3"
|
||||
version: "0.4.1"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1110,10 +1086,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a"
|
||||
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.0"
|
||||
version: "5.5.0"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -50,8 +50,8 @@ dependencies:
|
||||
vector_math: ^2.1.4
|
||||
permission_handler: ^11.3.0
|
||||
file_picker: ^8.0.0+1
|
||||
shadcn_ui: ^0.3.3
|
||||
modular_ui: ^0.0.5
|
||||
shadcn_ui: ^0.4.1
|
||||
flutter_carousel_widget: ^2.2.0
|
||||
|
||||
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
@@ -90,6 +90,9 @@ flutter:
|
||||
- assets/datasets/bus-blinds.csv
|
||||
- assets/audio/5-seconds-of-silence.mp3
|
||||
- assets/version.txt
|
||||
- assets/audio/R_RAIL_REPLACEMENT_SERVICE_001.mp3
|
||||
- assets/datasets/tube_stations.json
|
||||
- assets/audio/rail_replacement/
|
||||
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
|
||||
Reference in New Issue
Block a user