diff --git a/frontend/lib/pages/base_page.dart b/frontend/lib/layouts/scaffold.dart similarity index 67% rename from frontend/lib/pages/base_page.dart rename to frontend/lib/layouts/scaffold.dart index ce36f1f..e927e5f 100644 --- a/frontend/lib/pages/base_page.dart +++ b/frontend/lib/layouts/scaffold.dart @@ -1,72 +1,51 @@ -import 'package:anyway/main.dart'; -import 'package:anyway/modules/help_dialog.dart'; -import 'package:anyway/pages/current_trip.dart'; -import 'package:anyway/pages/settings.dart'; import 'package:flutter/material.dart'; import 'package:anyway/constants.dart'; -import 'package:anyway/structs/trip.dart'; +import 'package:anyway/main.dart'; +import 'package:anyway/modules/help_dialog.dart'; import 'package:anyway/modules/trips_saved_list.dart'; -import 'package:anyway/utils/load_trips.dart'; -import 'package:anyway/pages/new_trip_location.dart'; import 'package:anyway/pages/onboarding.dart'; +import 'package:anyway/pages/current_trip.dart'; +import 'package:anyway/pages/settings.dart'; +import 'package:anyway/pages/new_trip_location.dart'; - - -// BasePage is the scaffold that holds a child page and a side drawer -// The side drawer is the main way to switch between pages - -class BasePage extends StatefulWidget { - final Widget mainScreen; - final Widget title; - final List helpTexts; - - const BasePage({ - super.key, - required this.mainScreen, - this.title = const Text(APP_NAME), - this.helpTexts = const [], - }); - - @override - State createState() => _BasePageState(); -} - -class _BasePageState extends State { - - @override - Widget build(BuildContext context) { - savedTrips.loadTrips(); - - +mixin ScaffoldLayout on State { + Widget mainScaffold( + BuildContext context, + { + Widget child = const Text("emptiness"), + Widget title = const Text(APP_NAME), + List helpTexts = const [] + } + ) { return Scaffold( appBar: AppBar( - title: widget.title, + title: title, actions: [ IconButton( icon: const Icon(Icons.help), tooltip: 'Help', onPressed: () { - if (widget.helpTexts.isNotEmpty) { - helpDialog(context, widget.helpTexts[0], widget.helpTexts[1]); + if (helpTexts.isNotEmpty) { + helpDialog(context, helpTexts[0], helpTexts[1]); } } ), ], ), - body: Center(child: widget.mainScreen), + body: Center(child: child), drawer: Drawer( child: Column( children: [ Container( - decoration: BoxDecoration( + decoration: const BoxDecoration( gradient: APP_GRADIENT, ), height: 150, - child: Center( + child: const Center( child: Text( APP_NAME, style: TextStyle( @@ -81,8 +60,7 @@ class _BasePageState extends State { ListTile( title: const Text('Your Trips'), leading: const Icon(Icons.map), - // TODO: this is not working! - selected: widget.mainScreen is TripPage, + selected: widget is TripPage, onTap: () {}, trailing: ElevatedButton( onPressed: () { @@ -111,13 +89,12 @@ class _BasePageState extends State { const Divider(indent: 10, endIndent: 10), ListTile( title: const Text('How to use'), - leading: Icon(Icons.help), - // TODO: this is not working! - selected: widget.mainScreen is OnboardingPage, + leading: const Icon(Icons.help), + selected: widget is OnboardingPage, onTap: () { Navigator.of(context).push( MaterialPageRoute( - builder: (context) => OnboardingPage() + builder: (context) => const OnboardingPage() ) ); }, @@ -127,8 +104,7 @@ class _BasePageState extends State { ListTile( title: const Text('Settings'), leading: const Icon(Icons.settings), - // TODO: this is not working! - selected: widget.mainScreen is SettingsPage, + selected: widget is SettingsPage, onTap: () { Navigator.of(context).push( MaterialPageRoute( diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index 5f3dc49..ed4121f 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -1,24 +1,27 @@ +import 'package:flutter/material.dart'; + +import 'package:anyway/constants.dart'; import 'package:anyway/utils/get_first_page.dart'; import 'package:anyway/utils/load_trips.dart'; -import 'package:flutter/material.dart'; -import 'package:anyway/constants.dart'; + void main() => runApp(const App()); +// Some global variables final GlobalKey rootScaffoldMessengerKey = GlobalKey(); final SavedTrips savedTrips = SavedTrips(); +// the list of saved trips is then populated implicitly by getFirstPage() + class App extends StatelessWidget { const App({super.key}); @override - Widget build(BuildContext context) { - return MaterialApp( - title: APP_NAME, - home: getFirstPage(), - theme: APP_THEME, - scaffoldMessengerKey: rootScaffoldMessengerKey - ); - } + Widget build(BuildContext context) => MaterialApp( + title: APP_NAME, + home: getFirstPage(), + theme: APP_THEME, + scaffoldMessengerKey: rootScaffoldMessengerKey + ); } diff --git a/frontend/lib/modules/current_trip_landmarks_list.dart b/frontend/lib/modules/current_trip_landmarks_list.dart index c92e920..9ab4fbc 100644 --- a/frontend/lib/modules/current_trip_landmarks_list.dart +++ b/frontend/lib/modules/current_trip_landmarks_list.dart @@ -1,10 +1,9 @@ -import 'dart:developer'; -import 'package:anyway/modules/step_between_landmarks.dart'; import 'package:flutter/material.dart'; -import 'package:anyway/modules/landmark_card.dart'; import 'package:anyway/structs/landmark.dart'; import 'package:anyway/structs/trip.dart'; +import 'package:anyway/modules/step_between_landmarks.dart'; +import 'package:anyway/modules/landmark_card.dart'; // Returns a list of widgets that represent the landmarks matching the given selector @@ -35,4 +34,3 @@ List landmarksList(Trip trip, {required bool Function(Landmark) selector return children; } - diff --git a/frontend/lib/modules/current_trip_loading_indicator.dart b/frontend/lib/modules/current_trip_loading_indicator.dart index 96648a3..5eaf037 100644 --- a/frontend/lib/modules/current_trip_loading_indicator.dart +++ b/frontend/lib/modules/current_trip_loading_indicator.dart @@ -35,29 +35,29 @@ class _CurrentTripLoadingIndicatorState extends State _statusTextState(); + _StatusTextState createState() => _StatusTextState(); } -class _statusTextState extends State { +class _StatusTextState extends State { int statusIndex = 0; @override void initState() { super.initState(); - Future.delayed(Duration(seconds: 5), () { + Future.delayed(const Duration(seconds: 5), () { setState(() { statusIndex = (statusIndex + 1) % statusTexts.length; }); @@ -159,4 +159,3 @@ class _AnimatedGradientTextState extends State with Single ); } } - diff --git a/frontend/lib/modules/current_trip_map.dart b/frontend/lib/modules/current_trip_map.dart index 1802404..d1c6407 100644 --- a/frontend/lib/modules/current_trip_map.dart +++ b/frontend/lib/modules/current_trip_map.dart @@ -1,13 +1,14 @@ import 'dart:collection'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:widget_to_marker/widget_to_marker.dart'; import 'package:anyway/constants.dart'; -import 'package:anyway/modules/landmark_map_marker.dart'; -import 'package:flutter/material.dart'; import 'package:anyway/structs/landmark.dart'; import 'package:anyway/structs/trip.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:widget_to_marker/widget_to_marker.dart'; +import 'package:anyway/modules/landmark_map_marker.dart'; + class CurrentTripMap extends StatefulWidget { final Trip? trip; @@ -60,25 +61,29 @@ class _CurrentTripMapState extends State { } void setMapMarkers() async { - List landmarks = widget.trip?.landmarks.toList() ?? []; - Set markers = {}; + Iterator<(int, Landmark)> it = (widget.trip?.landmarks.toList() ?? []).indexed.iterator; - for (int i = 0; i < landmarks.length; i++) { - Landmark landmark = landmarks[i]; + while (it.moveNext()) { + int i = it.current.$1; + Landmark landmark = it.current.$2; + + MarkerId markerId = MarkerId("${landmark.uuid} - ${landmark.visited}"); List location = landmark.location; - Marker marker = Marker( - markerId: MarkerId(landmark.uuid), - position: LatLng(location[0], location[1]), - icon: await ThemedMarker(landmark: landmark, position: i).toBitmapDescriptor( - logicalSize: const Size(150, 150), - imageSize: const Size(150, 150), - ), - ); - markers.add(marker); + // only create a new marker, if there is no marker for this landmark + if (!mapMarkers.any((Marker marker) => marker.markerId == markerId)) { + Marker marker = Marker( + markerId: markerId, + position: LatLng(location[0], location[1]), + icon: await ThemedMarker(landmark: landmark, position: i).toBitmapDescriptor( + logicalSize: const Size(150, 150), + imageSize: const Size(150, 150), + ) + ); + setState(() { + mapMarkers.add(marker); + }); + } } - setState(() { - mapMarkers = markers; - }); } void setMapRoute() async { @@ -98,8 +103,8 @@ class _CurrentTripMapState extends State { Polyline stepLine = Polyline( polylineId: PolylineId('step-${landmark.uuid}'), points: step, - color: landmark.visited ? Colors.grey : PRIMARY_COLOR, - width: 5, + color: landmark.visited || (landmark.next?.visited ?? false) ? Colors.grey : PRIMARY_COLOR, + width: 5 ); polyLines.add(stepLine); } diff --git a/frontend/lib/modules/current_trip_panel.dart b/frontend/lib/modules/current_trip_panel.dart index 230c4c3..8e55b83 100644 --- a/frontend/lib/modules/current_trip_panel.dart +++ b/frontend/lib/modules/current_trip_panel.dart @@ -1,10 +1,12 @@ -import 'package:anyway/constants.dart'; -import 'package:anyway/modules/current_trip_error_message.dart'; -import 'package:anyway/modules/current_trip_loading_indicator.dart'; -import 'package:anyway/structs/landmark.dart'; import 'package:flutter/material.dart'; +import 'package:anyway/constants.dart'; + +import 'package:anyway/structs/landmark.dart'; import 'package:anyway/structs/trip.dart'; + +import 'package:anyway/modules/current_trip_error_message.dart'; +import 'package:anyway/modules/current_trip_loading_indicator.dart'; import 'package:anyway/modules/current_trip_summary.dart'; import 'package:anyway/modules/current_trip_save_button.dart'; import 'package:anyway/modules/current_trip_landmarks_list.dart'; @@ -74,20 +76,21 @@ class _CurrentTripPanelState extends State { child: Column( children: [ CurrentTripSummary(trip: widget.trip), - ExpansionTile( - leading: Icon(Icons.location_on), - title: Text('Visited Landmarks (tap to expand)'), - children: [ - ...landmarksList(widget.trip, selector: (Landmark landmark) => landmark.visited), - ], - visualDensity: VisualDensity.compact, - collapsedShape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + if (widget.trip.landmarks.where((Landmark landmark) => landmark.visited).isNotEmpty) + ExpansionTile( + leading: const Icon(Icons.location_on), + title: const Text('Visited Landmarks (tap to expand)'), + children: [ + ...landmarksList(widget.trip, selector: (Landmark landmark) => landmark.visited), + ], + visualDensity: VisualDensity.compact, + collapsedShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - ), ], ), ), @@ -108,4 +111,4 @@ class _CurrentTripPanelState extends State { } ); } -} +} diff --git a/frontend/lib/modules/current_trip_save_button.dart b/frontend/lib/modules/current_trip_save_button.dart index 0b8e773..64028e7 100644 --- a/frontend/lib/modules/current_trip_save_button.dart +++ b/frontend/lib/modules/current_trip_save_button.dart @@ -1,8 +1,8 @@ +import 'package:flutter/material.dart'; +import 'package:auto_size_text/auto_size_text.dart'; import 'package:anyway/main.dart'; import 'package:anyway/structs/trip.dart'; -import 'package:auto_size_text/auto_size_text.dart'; -import 'package:flutter/material.dart'; class saveButton extends StatefulWidget { @@ -52,4 +52,3 @@ class _saveButtonState extends State { ); } } - diff --git a/frontend/lib/modules/current_trip_summary.dart b/frontend/lib/modules/current_trip_summary.dart index bc46092..e7aa4a5 100644 --- a/frontend/lib/modules/current_trip_summary.dart +++ b/frontend/lib/modules/current_trip_summary.dart @@ -1,6 +1,7 @@ import 'package:anyway/structs/trip.dart'; import 'package:flutter/material.dart'; + class CurrentTripSummary extends StatefulWidget { final Trip trip; const CurrentTripSummary({ @@ -16,22 +17,22 @@ class _CurrentTripSummaryState extends State { @override Widget build(BuildContext context) { return Padding( - padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20), + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ - Icon(Icons.flag, size: 20), - Padding(padding: EdgeInsets.only(right: 10)), - Text('Stops: ${widget.trip.landmarks.length}', style: Theme.of(context).textTheme.bodyLarge,), + const Icon(Icons.flag, size: 20), + const Padding(padding: EdgeInsets.only(right: 10)), + Text('Stops: ${widget.trip.landmarks.length}', style: Theme.of(context).textTheme.bodyLarge), ] ), Row( children: [ - Icon(Icons.hourglass_bottom_rounded, size: 20), - Padding(padding: EdgeInsets.only(right: 10)), - Text('Duration: ${widget.trip.totalTime} minutes', style: Theme.of(context).textTheme.bodyLarge,), + const Icon(Icons.hourglass_bottom_rounded, size: 20), + const Padding(padding: EdgeInsets.only(right: 10)), + Text('Duration: ${widget.trip.totalTime} minutes', style: Theme.of(context).textTheme.bodyLarge), ] ), ], diff --git a/frontend/lib/modules/help_dialog.dart b/frontend/lib/modules/help_dialog.dart index 75db7b0..caf94aa 100644 --- a/frontend/lib/modules/help_dialog.dart +++ b/frontend/lib/modules/help_dialog.dart @@ -1,6 +1,6 @@ - import 'package:flutter/material.dart'; + Future helpDialog(BuildContext context, String title, String content) { return showDialog( context: context, diff --git a/frontend/lib/modules/landmark_card.dart b/frontend/lib/modules/landmark_card.dart index e3e8a75..9ef4875 100644 --- a/frontend/lib/modules/landmark_card.dart +++ b/frontend/lib/modules/landmark_card.dart @@ -1,12 +1,15 @@ -import 'package:anyway/constants.dart'; -import 'package:anyway/main.dart'; -import 'package:anyway/structs/trip.dart'; import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:url_launcher/url_launcher.dart'; +import 'package:anyway/constants.dart'; + +import 'package:anyway/main.dart'; +import 'package:anyway/structs/trip.dart'; +import 'package:url_launcher/url_launcher.dart'; import 'package:anyway/structs/landmark.dart'; + + class LandmarkCard extends StatefulWidget { final Landmark landmark; final Trip parentTrip; @@ -23,20 +26,11 @@ class LandmarkCard extends StatefulWidget { class _LandmarkCardState extends State { @override - Widget build(BuildContext context) { - if (widget.landmark.type == typeStart || widget.landmark.type == typeFinish) { - return TextButton.icon( - onPressed: () {}, - icon: widget.landmark.type.icon, - label: Text(widget.landmark.name), - ); - - } - + Widget build(BuildContext context) { return Container( constraints: BoxConstraints( - minHeight: 50, - maxHeight: 200, + // express the max height in terms text lines + maxHeight: 7 * (Theme.of(context).textTheme.titleMedium!.fontSize! + 10), ), child: Card( shape: RoundedRectangleBorder( @@ -79,23 +73,23 @@ class _LandmarkCardState extends State { ), ), ), - - Container( - color: PRIMARY_COLOR, - child: Center( - child: Padding( - padding: EdgeInsets.all(5), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 5, - children: [ - Icon(widget.landmark.type.icon.icon, size: 16), - Text(widget.landmark.type.name, style: TextStyle(fontWeight: FontWeight.bold)), - ], + if (widget.landmark.type != typeStart && widget.landmark.type != typeFinish) + Container( + color: PRIMARY_COLOR, + child: Center( + child: Padding( + padding: EdgeInsets.all(5), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 5, + children: [ + Icon(Icons.timer_outlined, size: 16), + Text("${widget.landmark.duration?.inMinutes} minutes"), + ], + ) ) - ) - ), - ) + ), + ) ], ) ), @@ -133,12 +127,6 @@ class _LandmarkCardState extends State { // show the type, the website, and the wikipedia link as buttons/labels in a row children: [ doneToggleButton(), - // if (widget.landmark.duration != null && widget.landmark.duration!.inMinutes > 0) - // TextButton.icon( - // onPressed: () {}, - // icon: Icon(Icons.hourglass_bottom), - // label: Text('${widget.landmark.duration!.inMinutes} minutes'), - // ), if (widget.landmark.websiteURL != null) websiteButton(), @@ -172,33 +160,35 @@ class _LandmarkCardState extends State { // open a browser with the website link await launchUrl(Uri.parse(widget.landmark.websiteURL!)); }, - icon: Icon(Icons.link), - label: Text('Website'), + icon: const Icon(Icons.link), + label: const Text('Website'), ); + Widget optionsButton () => PopupMenuButton( - icon: Icon(Icons.settings), + icon: const Icon(Icons.settings), style: TextButtonTheme.of(context).style, itemBuilder: (context) => [ PopupMenuItem( child: ListTile( - leading: Icon(Icons.delete), - title: Text('Delete'), + leading: const Icon(Icons.delete), + title: const Text('Delete'), onTap: () async { widget.parentTrip.removeLandmark(widget.landmark); rootScaffoldMessengerKey.currentState!.showSnackBar( - SnackBar(content: Text("We won't show ${widget.landmark.name} again")) + SnackBar(content: Text("${widget.landmark.name} won't be shown again")) ); }, ), ), PopupMenuItem( child: ListTile( - leading: Icon(Icons.star), - title: Text('Favorite'), + leading: const Icon(Icons.star), + title: const Text('Favorite'), onTap: () async { - // delete the landmark - // await deleteLandmark(widget.landmark); + rootScaffoldMessengerKey.currentState!.showSnackBar( + SnackBar(content: Text("Not implemented yet")) + ); }, ), ), diff --git a/frontend/lib/modules/new_trip_button.dart b/frontend/lib/modules/new_trip_button.dart index 9dca910..3eb5d3b 100644 --- a/frontend/lib/modules/new_trip_button.dart +++ b/frontend/lib/modules/new_trip_button.dart @@ -46,11 +46,11 @@ class _NewTripButtonState extends State { UserPreferences preferences = widget.preferences; if (preferences.nature.value == 0 && preferences.shopping.value == 0 && preferences.sightseeing.value == 0){ rootScaffoldMessengerKey.currentState!.showSnackBar( - SnackBar(content: Text("Please specify at least one preference")) + const SnackBar(content: Text("Please specify at least one preference")) ); } else if (preferences.maxTime.value == 0){ rootScaffoldMessengerKey.currentState!.showSnackBar( - SnackBar(content: Text("Please choose a longer duration")) + const SnackBar(content: Text("Please choose a longer duration")) ); } else { Trip trip = widget.trip; @@ -63,4 +63,3 @@ class _NewTripButtonState extends State { } } } - diff --git a/frontend/lib/modules/new_trip_map.dart b/frontend/lib/modules/new_trip_map.dart index 02d4174..e4e5400 100644 --- a/frontend/lib/modules/new_trip_map.dart +++ b/frontend/lib/modules/new_trip_map.dart @@ -1,14 +1,14 @@ // A map that allows the user to select a location for a new trip. -import 'dart:developer'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:widget_to_marker/widget_to_marker.dart'; import 'package:anyway/constants.dart'; -import 'package:anyway/modules/landmark_map_marker.dart'; -import 'package:anyway/structs/landmark.dart'; + import 'package:anyway/structs/trip.dart'; -import 'package:flutter/material.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:widget_to_marker/widget_to_marker.dart'; +import 'package:anyway/structs/landmark.dart'; +import 'package:anyway/modules/landmark_map_marker.dart'; class NewTripMap extends StatefulWidget { diff --git a/frontend/lib/modules/step_between_landmarks.dart b/frontend/lib/modules/step_between_landmarks.dart index b238cc1..739f1b6 100644 --- a/frontend/lib/modules/step_between_landmarks.dart +++ b/frontend/lib/modules/step_between_landmarks.dart @@ -1,7 +1,9 @@ -import 'package:anyway/structs/landmark.dart'; import 'package:flutter/material.dart'; + +import 'package:anyway/structs/landmark.dart'; import 'package:anyway/modules/map_chooser.dart'; + class StepBetweenLandmarks extends StatefulWidget { final Landmark current; final Landmark next; @@ -19,11 +21,15 @@ class StepBetweenLandmarks extends StatefulWidget { class _StepBetweenLandmarksState extends State { @override Widget build(BuildContext context) { - int time = widget.current.tripTime?.inMinutes ?? 0; + int? time = widget.current.tripTime?.inMinutes; + if (time != null && time < 1) { + time = 1; + } + return Container( - margin: EdgeInsets.all(10), - padding: EdgeInsets.all(10), - decoration: BoxDecoration( + margin: const EdgeInsets.all(10), + padding: const EdgeInsets.all(10), + decoration: const BoxDecoration( border: Border( left: BorderSide(width: 3.0, color: Colors.black), ), @@ -32,21 +38,22 @@ class _StepBetweenLandmarksState extends State { children: [ Column( children: [ - Icon(Icons.directions_walk), - Text("$time min", style: TextStyle(fontSize: 10)), + const Icon(Icons.directions_walk), + Text( + time == null ? "" : "About $time min", + style: const TextStyle(fontSize: 10) + ), ], ), - Spacer(), - ElevatedButton( + + const Spacer(), + + ElevatedButton.icon( onPressed: () async { showMapChooser(context, widget.current, widget.next); }, - child: Row( - children: [ - Icon(Icons.directions), - Text("Directions"), - ], - ), + icon: const Icon(Icons.directions), + label: const Text("Directions"), ) ], ), diff --git a/frontend/lib/pages/current_trip.dart b/frontend/lib/pages/current_trip.dart index 6f45e7f..8f70652 100644 --- a/frontend/lib/pages/current_trip.dart +++ b/frontend/lib/pages/current_trip.dart @@ -1,5 +1,5 @@ import 'package:anyway/constants.dart'; -import 'package:anyway/pages/base_page.dart'; +import 'package:anyway/layouts/scaffold.dart'; import 'package:flutter/material.dart'; import 'package:sliding_up_panel/sliding_up_panel.dart'; @@ -28,12 +28,13 @@ class TripPage extends StatefulWidget { -class _TripPageState extends State { +class _TripPageState extends State with ScaffoldLayout{ @override Widget build(BuildContext context) { - return BasePage( - mainScreen: SlidingUpPanel( + return mainScaffold( + context, + child: SlidingUpPanel( // use panelBuilder instead of panel so that we can reuse the scrollcontroller for the listview panelBuilder: (scrollcontroller) => CurrentTripPanel(controller: scrollcontroller, trip: widget.trip), // using collapsed and panelBuilder seems to show both at the same time, so we include the greeter in the panelBuilder @@ -58,9 +59,13 @@ class _TripPageState extends State { title: FutureBuilder( future: widget.trip.cityName, builder: (context, snapshot) => Text( - 'Your trip to ${snapshot.hasData ? snapshot.data! : "..."}', + 'Trip to ${snapshot.hasData ? snapshot.data! : "..."}', ) ), + helpTexts: [ + 'Current trip', + 'You can see and edit your current trip here. Swipe up from the bottom to see a detailed view of the recommendations.' + ], ); } } diff --git a/frontend/lib/pages/new_trip_location.dart b/frontend/lib/pages/new_trip_location.dart index 2fba16f..cca895d 100644 --- a/frontend/lib/pages/new_trip_location.dart +++ b/frontend/lib/pages/new_trip_location.dart @@ -1,5 +1,5 @@ +import 'package:anyway/layouts/scaffold.dart'; import 'package:anyway/modules/new_trip_options_button.dart'; -import 'package:anyway/pages/base_page.dart'; import 'package:flutter/material.dart'; import "package:anyway/structs/trip.dart"; @@ -14,7 +14,7 @@ class NewTripPage extends StatefulWidget { _NewTripPageState createState() => _NewTripPageState(); } -class _NewTripPageState extends State { +class _NewTripPageState extends State with ScaffoldLayout { final TextEditingController latController = TextEditingController(); final TextEditingController lonController = TextEditingController(); Trip trip = Trip(); @@ -23,8 +23,9 @@ class _NewTripPageState extends State { @override Widget build(BuildContext context) { // floating search bar and map as a background - return BasePage( - mainScreen: Scaffold( + return mainScaffold( + context, + child: Scaffold( body: Stack( children: [ NewTripMap(trip), diff --git a/frontend/lib/pages/new_trip_preferences.dart b/frontend/lib/pages/new_trip_preferences.dart index 15b7066..76aaf10 100644 --- a/frontend/lib/pages/new_trip_preferences.dart +++ b/frontend/lib/pages/new_trip_preferences.dart @@ -1,5 +1,5 @@ +import 'package:anyway/layouts/scaffold.dart'; import 'package:anyway/modules/new_trip_button.dart'; -import 'package:anyway/pages/base_page.dart'; import 'package:anyway/structs/preferences.dart'; import 'package:anyway/structs/trip.dart'; import 'package:flutter/cupertino.dart'; @@ -15,13 +15,14 @@ class NewTripPreferencesPage extends StatefulWidget { _NewTripPreferencesPageState createState() => _NewTripPreferencesPageState(); } -class _NewTripPreferencesPageState extends State { +class _NewTripPreferencesPageState extends State with ScaffoldLayout { UserPreferences preferences = UserPreferences(); @override Widget build(BuildContext context) { - return BasePage( - mainScreen: Scaffold( + return mainScaffold( + context, + child: Scaffold( body: ListView( children: [ // Center( @@ -41,23 +42,22 @@ class _NewTripPreferencesPageState extends State { // ) // ), - Center( - child: Padding( - padding: EdgeInsets.only(left: 10, right: 10, top: 20, bottom: 0), - child: Text('Tell us about your ideal trip.', style: TextStyle(fontSize: 18)) - ), + Center( + child: Padding( + padding: EdgeInsets.only(left: 10, right: 10, top: 20, bottom: 0), + child: Text('Tell us about your ideal trip.', style: TextStyle(fontSize: 18)) ), + ), - Divider(indent: 25, endIndent: 25, height: 50), + Divider(indent: 25, endIndent: 25, height: 50), - durationPicker(preferences.maxTime), + durationPicker(preferences.maxTime), - preferenceSliders([preferences.sightseeing, preferences.shopping, preferences.nature]), - ] - ), - floatingActionButton: NewTripButton(trip: widget.trip, preferences: preferences), + preferenceSliders([preferences.sightseeing, preferences.shopping, preferences.nature]), + ] + ), + floatingActionButton: NewTripButton(trip: widget.trip, preferences: preferences), ), - title: FutureBuilder( future: widget.trip.cityName, builder: (context, snapshot) => Text( diff --git a/frontend/lib/pages/settings.dart b/frontend/lib/pages/settings.dart index 3d5aff6..b5d3240 100644 --- a/frontend/lib/pages/settings.dart +++ b/frontend/lib/pages/settings.dart @@ -1,6 +1,6 @@ import 'package:anyway/constants.dart'; +import 'package:anyway/layouts/scaffold.dart'; import 'package:anyway/main.dart'; -import 'package:anyway/pages/base_page.dart'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -14,42 +14,41 @@ class SettingsPage extends StatefulWidget { _SettingsPageState createState() => _SettingsPageState(); } -class _SettingsPageState extends State { +class _SettingsPageState extends State with ScaffoldLayout { @override - Widget build(BuildContext context) { - return BasePage( - mainScreen: ListView( - padding: EdgeInsets.all(15), - children: [ - // First a round, centered image - Center( - child: CircleAvatar( - radius: 75, - child: Icon(Icons.settings, size: 100), - ) - ), - Center( - child: Text('Global settings', style: TextStyle(fontSize: 24)) - ), + Widget build (BuildContext context) => mainScaffold( + context, + child: ListView( + padding: EdgeInsets.all(15), + children: [ + // First a round, centered image + Center( + child: CircleAvatar( + radius: 75, + child: Icon(Icons.settings, size: 100), + ) + ), + Center( + child: Text('Global settings', style: TextStyle(fontSize: 24)) + ), - Divider(indent: 25, endIndent: 25, height: 50), + Divider(indent: 25, endIndent: 25, height: 50), - darkMode(), - setLocationUsage(), - setDebugMode(), + darkMode(), + setLocationUsage(), + setDebugMode(), - Divider(indent: 25, endIndent: 25, height: 50), + Divider(indent: 25, endIndent: 25, height: 50), - privacyInfo(), - ] - ), - title: Text('Settings'), - helpTexts: [ - 'Settings', - 'Preferences set in this page are global and will affect the entire application.' - ], - ); - } + privacyInfo(), + ] + ), + title: Text('Settings'), + helpTexts: [ + 'Settings', + 'Preferences set in this page are global and will affect the entire application.' + ], + ); Widget setDebugMode() { return Row( diff --git a/frontend/lib/structs/landmark.dart b/frontend/lib/structs/landmark.dart index a0e5905..8856047 100644 --- a/frontend/lib/structs/landmark.dart +++ b/frontend/lib/structs/landmark.dart @@ -70,10 +70,10 @@ final class Landmark extends LinkedListEntry{ final websiteURL = json['website_url'] as String?; final imageURL = json['image_url'] as String?; final description = json['description'] as String?; - var duration = Duration(minutes: json['duration'] ?? 0) as Duration?; - final visited = json['visited'] ?? false as bool; + var duration = Duration(minutes: json['duration']); + final visited = json['visited'] ?? false; var tripTime = Duration(minutes: json['time_to_reach_next'] ?? 0) as Duration?; - + return Landmark( uuid: uuid, name: name, diff --git a/frontend/lib/structs/trip.dart b/frontend/lib/structs/trip.dart index 8d816ca..e550540 100644 --- a/frontend/lib/structs/trip.dart +++ b/frontend/lib/structs/trip.dart @@ -29,6 +29,18 @@ class Trip with ChangeNotifier { } } + Future landmarkPosition (Landmark landmark) async { + int i = 0; + for (Landmark l in landmarks) { + if (l.uuid == landmark.uuid) { + return i; + } else if (l.type != typeStart && l.type != typeFinish) { + i++; + } + } + return -1; + } + Trip({ this.uuid = 'pending', diff --git a/frontend/lib/utils/fetch_trip.dart b/frontend/lib/utils/fetch_trip.dart index 4fc35e2..b243f0d 100644 --- a/frontend/lib/utils/fetch_trip.dart +++ b/frontend/lib/utils/fetch_trip.dart @@ -1,33 +1,33 @@ import "dart:convert"; import "dart:developer"; -import "package:anyway/utils/load_landmark_image.dart"; import 'package:dio/dio.dart'; import 'package:anyway/constants.dart'; +import "package:anyway/utils/load_landmark_image.dart"; import "package:anyway/structs/landmark.dart"; import "package:anyway/structs/trip.dart"; import "package:anyway/structs/preferences.dart"; Dio dio = Dio( - BaseOptions( - baseUrl: API_URL_BASE, - connectTimeout: const Duration(seconds: 5), - receiveTimeout: const Duration(seconds: 120), - // also accept 500 errors, since we cannot rule out that the server is at fault. We still want to gracefully handle these errors - validateStatus: (status) => status! <= 500, - receiveDataWhenStatusError: true, - // api is notoriously slow - // headers: { - // HttpHeaders.userAgentHeader: 'dio', - // 'api': '1.0.0', - // }, - contentType: Headers.jsonContentType, - responseType: ResponseType.json, - + BaseOptions( + baseUrl: API_URL_BASE, + connectTimeout: const Duration(seconds: 5), + receiveTimeout: const Duration(seconds: 120), + // also accept 500 errors, since we cannot rule out that the server is at fault. We still want to gracefully handle these errors + validateStatus: (status) => status! <= 500, + receiveDataWhenStatusError: true, + // api is notoriously slow + // headers: { + // HttpHeaders.userAgentHeader: 'dio', + // 'api': '1.0.0', + // }, + contentType: Headers.jsonContentType, + responseType: ResponseType.json, ), ); + fetchTrip( Trip trip, UserPreferences preferences, diff --git a/frontend/lib/utils/get_first_page.dart b/frontend/lib/utils/get_first_page.dart index e2dffd2..26f2787 100644 --- a/frontend/lib/utils/get_first_page.dart +++ b/frontend/lib/utils/get_first_page.dart @@ -1,11 +1,14 @@ -import 'package:anyway/pages/current_trip.dart'; -import 'package:anyway/pages/onboarding.dart'; -import 'package:anyway/structs/trip.dart'; -import 'package:anyway/utils/load_trips.dart'; +import 'package:anyway/main.dart'; import 'package:flutter/material.dart'; +import 'package:anyway/structs/trip.dart'; +import 'package:anyway/utils/load_trips.dart'; +import 'package:anyway/pages/current_trip.dart'; +import 'package:anyway/pages/onboarding.dart'; + + Widget getFirstPage() { - SavedTrips trips = SavedTrips(); + SavedTrips trips = savedTrips; trips.loadTrips(); return ListenableBuilder( @@ -15,7 +18,7 @@ Widget getFirstPage() { if (items.isNotEmpty) { return TripPage(trip: items[0]); } else { - return OnboardingPage(); + return const OnboardingPage(); } } );