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/commands.dart';
|
||||||
import 'package:bus_infotainment/backend/modules/synced_time.dart';
|
import 'package:bus_infotainment/backend/modules/synced_time.dart';
|
||||||
import 'package:bus_infotainment/backend/modules/tracker.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/tfl_datasets.dart';
|
||||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||||
import 'package:bus_infotainment/utils/delegates.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.");
|
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";
|
String sessionID = "test";
|
||||||
|
|
||||||
commandModule = CommandModule(sessionID);
|
commandModule = CommandModule(sessionID);
|
||||||
@@ -87,6 +94,7 @@ class LiveInformation {
|
|||||||
late AnnouncementModule announcementModule;
|
late AnnouncementModule announcementModule;
|
||||||
late SyncedTimeModule syncedTimeModule;
|
late SyncedTimeModule syncedTimeModule;
|
||||||
late TrackerModule trackerModule;
|
late TrackerModule trackerModule;
|
||||||
|
late TubeStations tubeStations;
|
||||||
|
|
||||||
// Important variables
|
// Important variables
|
||||||
BusRouteVariant? _currentRouteVariant;
|
BusRouteVariant? _currentRouteVariant;
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:bus_infotainment/audio_cache.dart';
|
import 'package:bus_infotainment/audio_cache.dart';
|
||||||
import 'package:bus_infotainment/backend/live_information.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/tfl_datasets.dart';
|
||||||
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
import 'package:bus_infotainment/utils/audio%20wrapper.dart';
|
||||||
import 'package:bus_infotainment/utils/delegates.dart';
|
import 'package:bus_infotainment/utils/delegates.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
import 'info_module.dart';
|
import 'info_module.dart';
|
||||||
|
|
||||||
@@ -33,12 +36,26 @@ class AnnouncementModule extends InfoModule {
|
|||||||
return _bundleBytes!;
|
return _bundleBytes!;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (kIsWeb) {
|
// Try to load them from shared preferences
|
||||||
throw Exception("Cannot load bundle bytes on web");
|
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";
|
String audioRoute = "R_${routeVariant.busRoute.routeNumber}_001.mp3";
|
||||||
print("Checkpoint 5");
|
print("Checkpoint 5");
|
||||||
await announcementCache.loadAnnouncementsFromBytes(await getBundleBytes(), [audioRoute, "R_RAIL_REPLACEMENT_SERVICE_001.mp3"]);
|
await announcementCache.loadAnnouncementsFromBytes(await getBundleBytes(), [audioRoute]);
|
||||||
print("Checkpoint 6");
|
print("Checkpoint 6");
|
||||||
AudioWrapperSource sourceRoute = !routeNumber.toLowerCase().startsWith("ul") ?
|
AudioWrapperSource sourceRoute = !routeNumber.toLowerCase().startsWith("ul") ?
|
||||||
AudioWrapperByteSource(announcementCache[audioRoute]!) :
|
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");
|
print("Checkpoint 6.1");
|
||||||
AudioWrapperSource sourceDestination = AudioWrapperByteSource(await routeVariant.destination!.getAudioBytes());
|
AudioWrapperSource sourceDestination = AudioWrapperByteSource(await routeVariant.destination!.getAudioBytes());
|
||||||
print("Checkpoint 7");
|
print("Checkpoint 7");
|
||||||
|
|||||||
@@ -90,7 +90,9 @@ class TrackerModule extends InfoModule {
|
|||||||
|
|
||||||
int stopIndex = liveInformation.getRouteVariant()!.busStops.indexOf(closestStop);
|
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}");
|
print("Closest stop is now: ${closestStop.formattedStopName}");
|
||||||
} else {
|
} 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) => TfL_Dataset_Test(),
|
||||||
'/': (context) => remaster.InitialStartup(),
|
'/': (context) => remaster.InitialStartup(),
|
||||||
'/application': (context) => TfL_Dataset_Test(),
|
'/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:google_fonts/google_fonts.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class InitialStartup extends StatefulWidget {
|
class InitialStartup extends StatefulWidget {
|
||||||
|
|
||||||
@@ -324,6 +325,8 @@ class _page3 extends InitialStartupPage {
|
|||||||
|
|
||||||
class _page3State extends State<_page3> {
|
class _page3State extends State<_page3> {
|
||||||
|
|
||||||
|
bool _loadingAudio = false;
|
||||||
|
|
||||||
Future<bool> _announcementsUploaded() async {
|
Future<bool> _announcementsUploaded() async {
|
||||||
try {
|
try {
|
||||||
Uint8List bytes = await LiveInformation().announcementModule.getBundleBytes();
|
Uint8List bytes = await LiveInformation().announcementModule.getBundleBytes();
|
||||||
@@ -393,6 +396,7 @@ class _page3State extends State<_page3> {
|
|||||||
height: 4,
|
height: 4,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
if (!_loadingAudio)
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _announcementsUploaded(),
|
future: _announcementsUploaded(),
|
||||||
builder: (context, val) {
|
builder: (context, val) {
|
||||||
@@ -412,6 +416,10 @@ class _page3State extends State<_page3> {
|
|||||||
text: Text(text),
|
text: Text(text),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_loadingAudio = true;
|
||||||
|
});
|
||||||
|
|
||||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||||
type: FileType.custom,
|
type: FileType.custom,
|
||||||
allowedExtensions: [
|
allowedExtensions: [
|
||||||
@@ -419,6 +427,8 @@ class _page3State extends State<_page3> {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
late Uint8List bytes;
|
late Uint8List bytes;
|
||||||
|
|
||||||
@@ -434,13 +444,25 @@ class _page3State extends State<_page3> {
|
|||||||
|
|
||||||
AnnouncementCache cache = LiveInformation().announcementModule.announcementCache;
|
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
|
// load a random announcement to ensure that the file is usable
|
||||||
await cache.loadAnnouncementsFromBytes(bytes, ["S_WALTHAMSTOW_CENTRAL_001.mp3"]);
|
await cache.loadAnnouncementsFromBytes(bytes, ["S_WALTHAMSTOW_CENTRAL_001.mp3"]);
|
||||||
|
|
||||||
setState(() {
|
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/InitialStartup.dart';
|
||||||
|
import 'package:bus_infotainment/remaster/dashboard.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:shadcn_ui/shadcn_ui.dart';
|
import 'package:shadcn_ui/shadcn_ui.dart';
|
||||||
|
|
||||||
@@ -17,7 +18,11 @@ class RemasteredApp extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
|
|
||||||
routes: {
|
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/pages/components/ibus_display.dart';
|
||||||
|
import 'package:bus_infotainment/tfl_datasets.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'dart:io' show Platform;
|
import 'dart:io' show Platform;
|
||||||
|
|
||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
|
||||||
import 'package:google_fonts/google_fonts.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 'package:text_scroll/text_scroll.dart';
|
||||||
|
|
||||||
|
import '../backend/modules/tube_info.dart';
|
||||||
|
|
||||||
Color rgb(int r, int g, int b) {
|
Color rgb(int r, int g, int b) {
|
||||||
return Color.fromRGBO(r, g, b, 1);
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// TODO: implement build
|
// TODO: implement build
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
body: Container(
|
||||||
|
|
||||||
backgroundColor: Colors.grey.shade900,
|
padding: EdgeInsets.all(16),
|
||||||
|
|
||||||
bottomNavigationBar: ((defaultTargetPlatform == TargetPlatform.android || defaultTargetPlatform == TargetPlatform.iOS)) ? null : BottomAppBar(
|
|
||||||
|
|
||||||
color: Colors.grey.shade800,
|
alignment: Alignment.center,
|
||||||
|
|
||||||
child: Row(
|
child: SizedBox(
|
||||||
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");
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
body: Column(
|
width: double.infinity,
|
||||||
children: [
|
|
||||||
|
|
||||||
Container(
|
child: Column(
|
||||||
|
|
||||||
margin: EdgeInsets.all(16),
|
mainAxisSize: MainAxisSize.min,
|
||||||
padding: EdgeInsets.all(8),
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|
||||||
decoration: BoxDecoration(
|
children: [
|
||||||
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
Text(
|
||||||
|
"Choose mode:",
|
||||||
color: Colors.grey.shade800
|
style: TextStyle(
|
||||||
|
fontSize: 32,
|
||||||
),
|
fontWeight: FontWeight.w600,
|
||||||
|
|
||||||
// height: 100,
|
|
||||||
|
|
||||||
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"),
|
|
||||||
),
|
|
||||||
|
|
||||||
NavigationRailDestination(
|
|
||||||
icon: Icon(Icons.search),
|
|
||||||
label: Text("Routes")
|
|
||||||
),
|
|
||||||
|
|
||||||
NavigationRailDestination(
|
|
||||||
icon: Icon(Icons.settings),
|
|
||||||
label: Text("Settings")
|
|
||||||
)
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
),
|
|
||||||
|
|
||||||
Expanded(
|
|
||||||
child: Container(
|
|
||||||
|
|
||||||
child: Column(
|
|
||||||
|
|
||||||
children: [
|
|
||||||
|
|
||||||
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,
|
|
||||||
//
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// ],
|
|
||||||
//
|
|
||||||
// )
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
],
|
|
||||||
|
|
||||||
),
|
|
||||||
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
|
),
|
||||||
],
|
|
||||||
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
children: [
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
height: 4,
|
||||||
|
),
|
||||||
|
|
||||||
|
ShadButton.secondary(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.pushNamed(context, "/routes");
|
||||||
|
},
|
||||||
|
text: Text("Continue"),
|
||||||
|
)
|
||||||
|
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
|
||||||
|
ShadCard(
|
||||||
|
title: Text("Multi mode"),
|
||||||
|
width: double.infinity,
|
||||||
|
description: Text(
|
||||||
|
"Choose this mode if you are using multiple devices. (Internet required)"
|
||||||
|
),
|
||||||
|
content: Column(
|
||||||
|
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
|
||||||
|
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");
|
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
|
// Replace space with underscore
|
||||||
stopName = stopName.replaceAll(' ', '_');
|
stopName = stopName.replaceAll(' ', '_');
|
||||||
|
stopName = stopName.replaceAll('-', '_');
|
||||||
|
|
||||||
// convert to all caps
|
// convert to all caps
|
||||||
stopName = stopName.toUpperCase();
|
stopName = stopName.toUpperCase();
|
||||||
@@ -263,6 +276,7 @@ class BusDestination {
|
|||||||
|
|
||||||
// Replace space with underscore
|
// Replace space with underscore
|
||||||
name = name.replaceAll(' ', '_');
|
name = name.replaceAll(' ', '_');
|
||||||
|
name = name.replaceAll('-', '_');
|
||||||
|
|
||||||
// convert to all caps
|
// convert to all caps
|
||||||
name = name.toUpperCase();
|
name = name.toUpperCase();
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ class NameBeautify {
|
|||||||
stopName = stopName.replaceAll(RegExp(r'\>.*\<'), '');
|
stopName = stopName.replaceAll(RegExp(r'\>.*\<'), '');
|
||||||
|
|
||||||
|
|
||||||
// remove any special characters except & and /
|
// remove any special characters except & and / and -
|
||||||
stopName = stopName.replaceAll(RegExp(r'[^a-zA-Z0-9&/ ]'), '');
|
stopName = stopName.replaceAll(RegExp(r'[^a-zA-Z0-9&\/\- ]'), '');
|
||||||
|
|
||||||
// remove any double spaces
|
// remove any double spaces
|
||||||
stopName = stopName.replaceAll(RegExp(r' '), ' ');
|
stopName = stopName.replaceAll(RegExp(r' '), ' ');
|
||||||
@@ -41,12 +41,17 @@ class NameBeautify {
|
|||||||
stopName = stopName.replaceAll(RegExp(phrase, caseSensitive: false), Longify[phrase]!);
|
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();
|
stopName = stopName.toLowerCase();
|
||||||
// Capitalify the first letter of each word
|
// Capitalify the first letter of each word
|
||||||
try {
|
try {
|
||||||
stopName = stopName.split(' ').map((word) => word[0].toUpperCase() + word.substring(1)).join(' ');
|
stopName = stopName.split(' ').map((word) => word[0].toUpperCase() + word.substring(1)).join(' ');
|
||||||
} catch (e) {}
|
} catch (e) {
|
||||||
|
print("Error capitalifying stop name: $stopName");
|
||||||
|
}
|
||||||
|
|
||||||
return stopName;
|
return stopName;
|
||||||
|
|
||||||
|
|||||||
@@ -25,14 +25,14 @@ class AudioWrapper {
|
|||||||
|
|
||||||
print("AudioWrapper mode: $mode");
|
print("AudioWrapper mode: $mode");
|
||||||
|
|
||||||
mode = AudioWrapper_Mode.Mobile;
|
mode = AudioWrapper_Mode.Web;
|
||||||
}
|
}
|
||||||
|
|
||||||
justaudio.AudioSource _convertSource_JustAudio(AudioWrapperSource source){
|
justaudio.AudioSource _convertSource_JustAudio(AudioWrapperSource source){
|
||||||
if (source is AudioWrapperByteSource){
|
if (source is AudioWrapperByteSource){
|
||||||
return _ByteSource(source.bytes);
|
return _ByteSource(source.bytes);
|
||||||
} else if (source is AudioWrapperAssetSource){
|
} else if (source is AudioWrapperAssetSource){
|
||||||
return justaudio.AudioSource.asset(source.assetPath);
|
return justaudio.AudioSource.asset("assets/" + source.assetPath);
|
||||||
} else {
|
} else {
|
||||||
throw Exception("Unknown source type");
|
throw Exception("Unknown source type");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
#include <audioplayers_linux/audioplayers_linux_plugin.h>
|
||||||
|
#include <rive_common/rive_plugin.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
#include <window_manager/window_manager_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 =
|
g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin");
|
||||||
audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar);
|
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 =
|
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
audioplayers_linux
|
audioplayers_linux
|
||||||
|
rive_common
|
||||||
screen_retriever
|
screen_retriever
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
window_manager
|
window_manager
|
||||||
|
|||||||
84
pubspec.lock
84
pubspec.lock
@@ -5,18 +5,18 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: appwrite
|
name: appwrite
|
||||||
sha256: "335faac24642aaf66627c21ce26a5c64bcbc3911e624c61b9dfbe0eec7be1342"
|
sha256: d95476adb1bbb72ae9eb31adffb9205b5cfe24abc4f483982eadceb396d433fb
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "12.0.1"
|
version: "12.0.3"
|
||||||
archive:
|
archive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: archive
|
name: archive
|
||||||
sha256: "22600aa1e926be775fa5fe7e6894e7fb3df9efda8891c73f70fb3262399a432d"
|
sha256: "0763b45fa9294197a2885c8567927e2830ade852e5c896fd4ab7e0e348d0f373"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.4.10"
|
version: "3.5.0"
|
||||||
args:
|
args:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -145,14 +145,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.18.0"
|
version: "1.18.0"
|
||||||
convert:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: convert
|
|
||||||
sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.1"
|
|
||||||
cookie_jar:
|
cookie_jar:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -197,10 +189,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: device_info_plus
|
name: device_info_plus
|
||||||
sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110"
|
sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "9.1.2"
|
version: "10.1.0"
|
||||||
device_info_plus_platform_interface:
|
device_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -245,10 +237,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: file_picker
|
name: file_picker
|
||||||
sha256: b6283d7387310ad83bc4f3bc245b75d223a032ae6eba275afcd585de2b9a1476
|
sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.0.1"
|
version: "8.0.3"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -270,6 +262,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.5.0"
|
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:
|
flutter_lints:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@@ -437,14 +437,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.18.1"
|
version: "0.18.1"
|
||||||
js:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: js
|
|
||||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "0.7.1"
|
|
||||||
just_audio:
|
just_audio:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -473,10 +465,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: just_audio_windows
|
name: just_audio_windows
|
||||||
sha256: "7b8801f3987e98a2002cd23b5600b2daf162248ff1413266fb44c84448c1c0d3"
|
sha256: "48ab2dec79cf6097550602fe07b1a644f341450e138dc8fdc23e42ce0ed2d928"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.1"
|
||||||
latlong2:
|
latlong2:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -573,14 +565,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.0"
|
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:
|
ntp:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -593,18 +577,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus
|
name: package_info_plus
|
||||||
sha256: "7e76fad405b3e4016cd39d08f455a4eb5199723cf594cd1b8916d47140d93017"
|
sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.2.0"
|
version: "8.0.0"
|
||||||
package_info_plus_platform_interface:
|
package_info_plus_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: package_info_plus_platform_interface
|
name: package_info_plus_platform_interface
|
||||||
sha256: "9bc8ba46813a4cc42c66ab781470711781940780fd8beddd0c3da62506d3a6c6"
|
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "3.0.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -741,14 +725,6 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.8"
|
||||||
pointycastle:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: pointycastle
|
|
||||||
sha256: "79fbafed02cfdbe85ef3fd06c7f4bc2cbcba0177e61b765264853d4253b21744"
|
|
||||||
url: "https://pub.dev"
|
|
||||||
source: hosted
|
|
||||||
version: "3.9.0"
|
|
||||||
polylabel:
|
polylabel:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -769,18 +745,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: rive
|
name: rive
|
||||||
sha256: ec44b6cf7341e21727c4b0e762f4ac82f9a45f7e52df3ebad2d1289a726fbaaf
|
sha256: "95690a0fb4f6e195c53b217ab3cc0e0b0f443c670adbdee9d57d636a36b82b18"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.13.1"
|
version: "0.13.2"
|
||||||
rive_common:
|
rive_common:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: rive_common
|
name: rive_common
|
||||||
sha256: "0f070bc0e764c570abd8b34d744ef30d1292bd4051f2e0951bbda755875fce6a"
|
sha256: "3eee68fcab3e0882090cea5a8cf7acea7967f469a34a2580322575603b094435"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3"
|
version: "0.4.5"
|
||||||
rxdart:
|
rxdart:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -801,10 +777,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shadcn_ui
|
name: shadcn_ui
|
||||||
sha256: "06c25555efbabe6d1d6acd6397bff4940e931d53ac3448e3a4f6a2287ab6798d"
|
sha256: df9aedbd18bd60160c1c54717eef47fe4f90422f843070ccbc32786193803f7d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.3"
|
version: "0.4.1"
|
||||||
shared_preferences:
|
shared_preferences:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -1110,10 +1086,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: win32
|
name: win32
|
||||||
sha256: "0a989dc7ca2bb51eac91e8fd00851297cfffd641aa7538b165c62637ca0eaa4a"
|
sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.4.0"
|
version: "5.5.0"
|
||||||
win32_registry:
|
win32_registry:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ dependencies:
|
|||||||
vector_math: ^2.1.4
|
vector_math: ^2.1.4
|
||||||
permission_handler: ^11.3.0
|
permission_handler: ^11.3.0
|
||||||
file_picker: ^8.0.0+1
|
file_picker: ^8.0.0+1
|
||||||
shadcn_ui: ^0.3.3
|
shadcn_ui: ^0.4.1
|
||||||
modular_ui: ^0.0.5
|
flutter_carousel_widget: ^2.2.0
|
||||||
|
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
@@ -90,6 +90,9 @@ flutter:
|
|||||||
- assets/datasets/bus-blinds.csv
|
- assets/datasets/bus-blinds.csv
|
||||||
- assets/audio/5-seconds-of-silence.mp3
|
- assets/audio/5-seconds-of-silence.mp3
|
||||||
- assets/version.txt
|
- 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_burr.jpeg
|
||||||
# - images/a_dot_ham.jpeg
|
# - images/a_dot_ham.jpeg
|
||||||
|
|||||||
Reference in New Issue
Block a user