510 lines
12 KiB
Dart
510 lines
12 KiB
Dart
|
|
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 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(
|
|
|
|
padding: EdgeInsets.all(16),
|
|
|
|
alignment: Alignment.center,
|
|
|
|
child: SizedBox(
|
|
|
|
width: double.infinity,
|
|
|
|
child: Column(
|
|
|
|
mainAxisSize: MainAxisSize.min,
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Text(
|
|
"Choose mode:",
|
|
style: TextStyle(
|
|
fontSize: 32,
|
|
fontWeight: FontWeight.w600,
|
|
)
|
|
),
|
|
|
|
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"),
|
|
|
|
),
|
|
*/ |