Compare commits
5 Commits
662a1ae157
...
main
Author | SHA1 | Date | |
---|---|---|---|
54cfe936c3 | |||
2a780f13c7 | |||
7b6a276c21 | |||
3f0342b6cb | |||
513b2d2f14 |
@@ -14,8 +14,9 @@ PODS:
|
|||||||
- geolocator_apple (1.2.0):
|
- geolocator_apple (1.2.0):
|
||||||
- Flutter
|
- Flutter
|
||||||
- OrderedSet (5.0.0)
|
- OrderedSet (5.0.0)
|
||||||
- path_provider_ios (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
- url_launcher_ios (0.0.1):
|
- url_launcher_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
|
||||||
@@ -25,7 +26,7 @@ DEPENDENCIES:
|
|||||||
- flutter_compass (from `.symlinks/plugins/flutter_compass/ios`)
|
- flutter_compass (from `.symlinks/plugins/flutter_compass/ios`)
|
||||||
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
|
- flutter_inappwebview (from `.symlinks/plugins/flutter_inappwebview/ios`)
|
||||||
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`)
|
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/ios`)
|
||||||
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
@@ -43,8 +44,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/flutter_inappwebview/ios"
|
:path: ".symlinks/plugins/flutter_inappwebview/ios"
|
||||||
geolocator_apple:
|
geolocator_apple:
|
||||||
:path: ".symlinks/plugins/geolocator_apple/ios"
|
:path: ".symlinks/plugins/geolocator_apple/ios"
|
||||||
path_provider_ios:
|
path_provider_foundation:
|
||||||
:path: ".symlinks/plugins/path_provider_ios/ios"
|
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||||
|
|
||||||
@@ -55,9 +56,9 @@ SPEC CHECKSUMS:
|
|||||||
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
|
flutter_inappwebview: bfd58618f49dc62f2676de690fc6dcda1d6c3721
|
||||||
geolocator_apple: cc556e6844d508c95df1e87e3ea6fa4e58c50401
|
geolocator_apple: cc556e6844d508c95df1e87e3ea6fa4e58c50401
|
||||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||||
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
|
path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
|
||||||
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
|
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||||
|
|
||||||
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
||||||
|
|
||||||
COCOAPODS: 1.11.3
|
COCOAPODS: 1.12.1
|
||||||
|
@@ -156,7 +156,7 @@
|
|||||||
97C146E61CF9000F007C117D /* Project object */ = {
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 1300;
|
LastUpgradeCheck = 1430;
|
||||||
ORGANIZATIONNAME = "";
|
ORGANIZATIONNAME = "";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
97C146ED1CF9000F007C117D = {
|
97C146ED1CF9000F007C117D = {
|
||||||
@@ -205,6 +205,7 @@
|
|||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
|
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
|
||||||
);
|
);
|
||||||
name = "Thin Binary";
|
name = "Thin Binary";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "1300"
|
LastUpgradeVersion = "1430"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@@ -57,16 +57,7 @@ class _AppPageLayoutState extends State<AppPageLayout> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: AnnotatedRegion<SystemUiOverlayStyle>(
|
body: AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: constructOverlayStyle(dark: widget.darkStatusBar),
|
||||||
statusBarColor: Colors.transparent,
|
|
||||||
systemNavigationBarContrastEnforced: true,
|
|
||||||
statusBarIconBrightness: widget.darkStatusBar
|
|
||||||
? Brightness.dark
|
|
||||||
: Brightness.light,
|
|
||||||
statusBarBrightness: widget.darkStatusBar
|
|
||||||
? Brightness.light
|
|
||||||
: Brightness.dark,
|
|
||||||
),
|
|
||||||
child: Container(
|
child: Container(
|
||||||
color: widget.backgroundColor ?? Colors.grey[100],
|
color: widget.backgroundColor ?? Colors.grey[100],
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
|
@@ -35,15 +35,14 @@ class _MainLayoutState extends State<MainLayout> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AutoTabsScaffold(
|
return AutoTabsScaffold(
|
||||||
|
animationDuration: const Duration(milliseconds: 150),
|
||||||
routes: const [
|
routes: const [
|
||||||
HomePageRouter(children: [
|
HomePageRouter(children: [
|
||||||
HomeRoute(),
|
HomeRoute(),
|
||||||
]),
|
]),
|
||||||
MapRoute(),
|
MapRoute(),
|
||||||
EventsRoute(),
|
EventsRoute(),
|
||||||
InfoPageRouter(children: [
|
InfoPageRouter(children: [InfoRoute()]),
|
||||||
InfoRoute()
|
|
||||||
]),
|
|
||||||
],
|
],
|
||||||
bottomNavigationBuilder: (_, tabsRouter) {
|
bottomNavigationBuilder: (_, tabsRouter) {
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
|
@@ -4,6 +4,7 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:furman_now/src/routes/index.gr.dart';
|
import 'package:furman_now/src/routes/index.gr.dart';
|
||||||
import 'package:furman_now/src/screens/home/content.dart';
|
import 'package:furman_now/src/screens/home/content.dart';
|
||||||
import 'package:furman_now/src/screens/home/state.dart';
|
import 'package:furman_now/src/screens/home/state.dart';
|
||||||
|
import 'package:furman_now/src/utils/theme.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class HomeScreenProvider extends StatelessWidget {
|
class HomeScreenProvider extends StatelessWidget {
|
||||||
@@ -101,7 +102,7 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: AnnotatedRegion<SystemUiOverlayStyle>(
|
body: AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle.light,
|
value: constructOverlayStyle(),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: const Color(0xffb7acc9),
|
color: const Color(0xffb7acc9),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
@@ -121,7 +122,10 @@ class _HomeScreenState extends State<HomeScreen> {
|
|||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: overscrollBottomAmount + 30,
|
height: overscrollBottomAmount + 30,
|
||||||
|
decoration: BoxDecoration(
|
||||||
color: Colors.grey.shade50,
|
color: Colors.grey.shade50,
|
||||||
|
border: Border.all(width: 0, color: Colors.white),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Consumer<HomePageState>(
|
Consumer<HomePageState>(
|
||||||
|
61
lib/src/services/transportation/route.dart
Normal file
61
lib/src/services/transportation/route.dart
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_map/flutter_map.dart';
|
||||||
|
import 'package:furman_now/src/widgets/map/route_marker.dart';
|
||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
|
||||||
|
class TransportationRoute {
|
||||||
|
final Polyline route;
|
||||||
|
final List<Stop> stops;
|
||||||
|
|
||||||
|
TransportationRoute({
|
||||||
|
required this.route,
|
||||||
|
required this.stops,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory TransportationRoute.fromPoints({
|
||||||
|
required List<LatLng> points,
|
||||||
|
required Color color,
|
||||||
|
required List<Stop> stops,
|
||||||
|
}) {
|
||||||
|
var route = Polyline(
|
||||||
|
points: points,
|
||||||
|
color: color,
|
||||||
|
strokeWidth: 4,
|
||||||
|
);
|
||||||
|
return TransportationRoute(
|
||||||
|
route: route,
|
||||||
|
stops: stops,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Stop {
|
||||||
|
int id;
|
||||||
|
String name;
|
||||||
|
LatLng location;
|
||||||
|
|
||||||
|
Stop({
|
||||||
|
required this.id,
|
||||||
|
required this.name,
|
||||||
|
required this.location,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Stop.fromLiveSafeJson(Map<String, dynamic> json) {
|
||||||
|
return Stop(
|
||||||
|
id: json["AddressID"],
|
||||||
|
name: json["Description"],
|
||||||
|
location: LatLng(json["Latitude"], json["Longitude"]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Marker toMarker({required Color color}) {
|
||||||
|
return Marker(
|
||||||
|
point: location,
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
builder: (context) => RouteMarker(color: color),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -1,3 +1,48 @@
|
|||||||
class TransportationSafeRideShuttleService {
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:furman_now/src/utils/decode_polyline.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import 'route.dart';
|
||||||
|
|
||||||
|
class TransportationSafeRideShuttleService {
|
||||||
|
static const service =
|
||||||
|
"https://furmansaferide.ridesystems.net/Services/JSONPRelay.svc";
|
||||||
|
|
||||||
|
static const apiKey = "8882812681";
|
||||||
|
|
||||||
|
static Future<http.Response> _serviceRequest(
|
||||||
|
String endpoint, Map<String, dynamic>? queryParameters) async {
|
||||||
|
Uri serviceUri = Uri.https(
|
||||||
|
'furmansaferide.ridesystems.net',
|
||||||
|
"/Services/JSONPRelay.svc/$endpoint",
|
||||||
|
{...?queryParameters, 'apiKey': apiKey});
|
||||||
|
return http.get(serviceUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<TransportationRoute> fetchShuttleRoute() async {
|
||||||
|
final response = await _serviceRequest(
|
||||||
|
'GetRoutesForMapWithScheduleWithEncodedLine', {'isDispatch': 'false'});
|
||||||
|
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
// If the server did return a 200 OK response,
|
||||||
|
// then parse the JSON.
|
||||||
|
final json = jsonDecode(response.body);
|
||||||
|
String encodedPolyline = json[0]["EncodedPolyline"];
|
||||||
|
final points = decodeEncodedPolyline(encodedPolyline);
|
||||||
|
var stops = (json[0]["Stops"] as List<dynamic>)
|
||||||
|
.map((e) => Stop.fromLiveSafeJson(e))
|
||||||
|
.toList();
|
||||||
|
return TransportationRoute.fromPoints(
|
||||||
|
points: points,
|
||||||
|
color: Colors.red.shade300,
|
||||||
|
stops: stops,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// If the server did not return a 200 OK response,
|
||||||
|
// then throw an exception.
|
||||||
|
throw Exception('Failed to load SafeRide shuttle route.');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
20
lib/src/services/transportation/vehicle.dart
Normal file
20
lib/src/services/transportation/vehicle.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
abstract class TransportationVehicle {
|
||||||
|
// name of the vehicle
|
||||||
|
String get name;
|
||||||
|
|
||||||
|
// is the vehicle currently running?
|
||||||
|
VehicleStatus get status;
|
||||||
|
|
||||||
|
// name of vehicle's next stop
|
||||||
|
String get nextStop;
|
||||||
|
|
||||||
|
// vehicle icon
|
||||||
|
IconData get icon;
|
||||||
|
|
||||||
|
// vehicle route
|
||||||
|
TransitionRoute get route;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum VehicleStatus { running, stopped }
|
34
lib/src/utils/decode_polyline.dart
Normal file
34
lib/src/utils/decode_polyline.dart
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import 'package:latlong2/latlong.dart';
|
||||||
|
|
||||||
|
/// Decodes the an encoded polyline using the Encoded Polyline Algorithm Format
|
||||||
|
/// for more info about the algorithm check
|
||||||
|
/// https://developers.google.com/maps/documentation/utilities/polylinealgorithm
|
||||||
|
List<LatLng> decodeEncodedPolyline(String encoded) {
|
||||||
|
List<LatLng> poly = [];
|
||||||
|
int index = 0, len = encoded.length;
|
||||||
|
int lat = 0, lng = 0;
|
||||||
|
|
||||||
|
while (index < len) {
|
||||||
|
int b, shift = 0, result = 0;
|
||||||
|
do {
|
||||||
|
b = encoded.codeUnitAt(index++) - 63;
|
||||||
|
result |= (b & 0x1f) << shift;
|
||||||
|
shift += 5;
|
||||||
|
} while (b >= 0x20);
|
||||||
|
int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
|
||||||
|
lat += dlat;
|
||||||
|
|
||||||
|
shift = 0;
|
||||||
|
result = 0;
|
||||||
|
do {
|
||||||
|
b = encoded.codeUnitAt(index++) - 63;
|
||||||
|
result |= (b & 0x1f) << shift;
|
||||||
|
shift += 5;
|
||||||
|
} while (b >= 0x20);
|
||||||
|
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
|
||||||
|
lng += dlng;
|
||||||
|
LatLng p = LatLng((lat / 1E5).toDouble(), (lng / 1E5).toDouble());
|
||||||
|
poly.add(p);
|
||||||
|
}
|
||||||
|
return poly;
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
ThemeData _baseTheme = ThemeData(
|
ThemeData _baseTheme = ThemeData(
|
||||||
@@ -47,5 +48,14 @@ ThemeData myFurmanTheme = _baseTheme.copyWith(
|
|||||||
textTheme: GoogleFonts.interTextTheme(_baseTheme.textTheme),
|
textTheme: GoogleFonts.interTextTheme(_baseTheme.textTheme),
|
||||||
);
|
);
|
||||||
|
|
||||||
var furmanTextStyle = (TextStyle baseStyle) =>
|
var furmanTextStyle =
|
||||||
GoogleFonts.inter(textStyle: baseStyle);
|
(TextStyle baseStyle) => GoogleFonts.inter(textStyle: baseStyle);
|
||||||
|
|
||||||
|
SystemUiOverlayStyle constructOverlayStyle({bool dark = false}) {
|
||||||
|
return SystemUiOverlayStyle(
|
||||||
|
statusBarColor: Colors.transparent,
|
||||||
|
systemNavigationBarContrastEnforced: true,
|
||||||
|
statusBarIconBrightness: dark ? Brightness.dark : Brightness.light,
|
||||||
|
statusBarBrightness: dark ? Brightness.light : Brightness.dark,
|
||||||
|
systemNavigationBarColor: Colors.grey.shade50);
|
||||||
|
}
|
||||||
|
472
pubspec.lock
472
pubspec.lock
File diff suppressed because it is too large
Load Diff
@@ -35,7 +35,7 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
english_words: ^4.0.0
|
english_words: ^4.0.0
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
google_fonts: ^3.0.1
|
google_fonts: ^4.0.4
|
||||||
http: ^0.13.5
|
http: ^0.13.5
|
||||||
convert: ^3.0.2
|
convert: ^3.0.2
|
||||||
crypto: ^3.0.2
|
crypto: ^3.0.2
|
||||||
|
22
test/service_tests/transportation/saferide_shuttle_test.dart
Normal file
22
test/service_tests/transportation/saferide_shuttle_test.dart
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import "package:furman_now/src/services/transportation/saferide_shuttle.dart";
|
||||||
|
import "package:test/test.dart";
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test("shuttle status is fetched successfully", () async {
|
||||||
|
var route = await TransportationSafeRideShuttleService.fetchShuttleRoute();
|
||||||
|
|
||||||
|
// ensure polyline has points
|
||||||
|
expect(route.route.points.length, isNonZero);
|
||||||
|
// ensure stops are listed
|
||||||
|
expect(route.stops.length, isNonZero);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("shuttle route is fetched successfully", () async {
|
||||||
|
var route = await TransportationSafeRideShuttleService.fetchShuttleRoute();
|
||||||
|
|
||||||
|
// ensure polyline has points
|
||||||
|
expect(route.route.points.length, isNonZero);
|
||||||
|
// ensure stops are listed
|
||||||
|
expect(route.stops.length, isNonZero);
|
||||||
|
});
|
||||||
|
}
|
Reference in New Issue
Block a user