From 311b1c2218b08a6b1bd94cd58340e94e46723d1e Mon Sep 17 00:00:00 2001 From: Remy Moll Date: Fri, 9 Aug 2024 00:48:45 +0200 Subject: [PATCH 1/4] location picker and ui fixes --- frontend/lib/constants.dart | 1 + frontend/lib/layout.dart | 9 +- frontend/lib/main.dart | 3 + frontend/lib/modules/landmark_card.dart | 12 +- frontend/lib/modules/landmarks_list.dart | 64 +++++++ frontend/lib/modules/landmarks_overview.dart | 168 ------------------ frontend/lib/modules/map.dart | 77 +------- frontend/lib/modules/new_trip_button.dart | 78 ++++++++ .../lib/modules/new_trip_location_search.dart | 64 +++++++ frontend/lib/modules/new_trip_map.dart | 86 +++++++++ frontend/lib/modules/save_button.dart | 33 ++++ .../lib/modules/step_between_landmarks.dart | 60 +++++++ frontend/lib/modules/themed_marker.dart | 68 +++++++ ...ps_overview.dart => trips_saved_list.dart} | 0 frontend/lib/pages/new_trip.dart | 81 +++------ frontend/lib/pages/overview.dart | 82 --------- frontend/lib/pages/trip.dart | 84 +++++++++ frontend/lib/structs/trip.dart | 13 +- 18 files changed, 588 insertions(+), 395 deletions(-) create mode 100644 frontend/lib/modules/landmarks_list.dart delete mode 100644 frontend/lib/modules/landmarks_overview.dart create mode 100644 frontend/lib/modules/new_trip_button.dart create mode 100644 frontend/lib/modules/new_trip_location_search.dart create mode 100644 frontend/lib/modules/new_trip_map.dart create mode 100644 frontend/lib/modules/save_button.dart create mode 100644 frontend/lib/modules/step_between_landmarks.dart create mode 100644 frontend/lib/modules/themed_marker.dart rename frontend/lib/modules/{trips_overview.dart => trips_saved_list.dart} (100%) delete mode 100644 frontend/lib/pages/overview.dart create mode 100644 frontend/lib/pages/trip.dart diff --git a/frontend/lib/constants.dart b/frontend/lib/constants.dart index efa0a1f..973aec9 100644 --- a/frontend/lib/constants.dart +++ b/frontend/lib/constants.dart @@ -2,3 +2,4 @@ const String APP_NAME = 'AnyWay'; String API_URL_BASE = 'https://anyway.kluster.moll.re'; +const String MAP_ID = '41c21ac9b81dbfd8'; diff --git a/frontend/lib/layout.dart b/frontend/lib/layout.dart index 7a214d3..be398c0 100644 --- a/frontend/lib/layout.dart +++ b/frontend/lib/layout.dart @@ -6,14 +6,17 @@ import 'package:flutter/material.dart'; import 'package:anyway/constants.dart'; import 'package:anyway/structs/trip.dart'; -import 'package:anyway/modules/trips_overview.dart'; +import 'package:anyway/modules/trips_saved_list.dart'; import 'package:anyway/utils/load_trips.dart'; import 'package:anyway/pages/new_trip.dart'; import 'package:anyway/pages/tutorial.dart'; -import 'package:anyway/pages/overview.dart'; +import 'package:anyway/pages/trip.dart'; import 'package:anyway/pages/profile.dart'; + + + // BasePage is the scaffold that holds all other pages // A side drawer is used to switch between pages class BasePage extends StatefulWidget { @@ -39,7 +42,7 @@ class _BasePageState extends State { if (widget.mainScreen == "map") { - currentView = NavigationOverview(trip: widget.trip ?? getFirstTrip(trips)); + currentView = TripPage(trip: widget.trip ?? getFirstTrip(trips)); } else if (widget.mainScreen == "tutorial") { currentView = TutorialPage(); } else if (widget.mainScreen == "profile") { diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index 44c3922..9e2f631 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -4,6 +4,8 @@ import 'package:anyway/layout.dart'; void main() => runApp(const App()); +final GlobalKey rootScaffoldMessengerKey = GlobalKey(); + class App extends StatelessWidget { const App({super.key}); @@ -14,6 +16,7 @@ class App extends StatelessWidget { title: APP_NAME, home: BasePage(mainScreen: "map"), theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.red[600]), + scaffoldMessengerKey: rootScaffoldMessengerKey ); } } diff --git a/frontend/lib/modules/landmark_card.dart b/frontend/lib/modules/landmark_card.dart index 579ebca..636153c 100644 --- a/frontend/lib/modules/landmark_card.dart +++ b/frontend/lib/modules/landmark_card.dart @@ -1,17 +1,19 @@ -import 'package:anyway/structs/landmark.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; +import 'package:cached_network_image/cached_network_image.dart'; + +import 'package:anyway/structs/landmark.dart'; class LandmarkCard extends StatefulWidget { final Landmark landmark; + + LandmarkCard(this.landmark); + @override _LandmarkCardState createState() => _LandmarkCardState(); - - LandmarkCard(this.landmark); - } + class _LandmarkCardState extends State { @override Widget build(BuildContext context) { diff --git a/frontend/lib/modules/landmarks_list.dart b/frontend/lib/modules/landmarks_list.dart new file mode 100644 index 0000000..097b700 --- /dev/null +++ b/frontend/lib/modules/landmarks_list.dart @@ -0,0 +1,64 @@ +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/main.dart'; + + + +List landmarksList(Trip trip) { + log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks"); + + List children = []; + + log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks"); + + if (trip.landmarks.isEmpty || trip.landmarks.length <= 1 && trip.landmarks.first.type == start ) { + children.add( + const Text("No landmarks in this trip"), + ); + return children; + } + + for (Landmark landmark in trip.landmarks) { + children.add( + Dismissible( + key: ValueKey(landmark.hashCode), + child: LandmarkCard(landmark), + dismissThresholds: {DismissDirection.endToStart: 0.95, DismissDirection.startToEnd: 0.95}, + onDismissed: (direction) { + log('Removing ${landmark.name}'); + trip.removeLandmark(landmark); + // Then show a snackbar + + rootScaffoldMessengerKey.currentState!.showSnackBar( + SnackBar(content: Text("We won't show ${landmark.name} again")) + ); + }, + + background: Container(color: Colors.red), + secondaryBackground: Container( + color: Colors.red, + child: Icon( + Icons.delete, + color: Colors.white, + ), + padding: EdgeInsets.all(15), + alignment: Alignment.centerRight, + ), + ) + ); + + if (landmark.next != null) { + children.add( + StepBetweenLandmarks(current: landmark, next: landmark.next!) + ); + } + } + + return children; +} + diff --git a/frontend/lib/modules/landmarks_overview.dart b/frontend/lib/modules/landmarks_overview.dart deleted file mode 100644 index a368e3d..0000000 --- a/frontend/lib/modules/landmarks_overview.dart +++ /dev/null @@ -1,168 +0,0 @@ -import 'dart:developer'; -import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import 'package:anyway/modules/landmark_card.dart'; -import 'package:anyway/structs/landmark.dart'; -import 'package:anyway/structs/trip.dart'; - - - - -class LandmarksOverview extends StatefulWidget { - final Trip? trip; - const LandmarksOverview({super.key, this.trip}); - - @override - State createState() => _LandmarksOverviewState(); -} - -class _LandmarksOverviewState extends State { - - @override - Widget build(BuildContext context) { - return ListenableBuilder( - listenable: widget.trip!, - builder: (BuildContext context, Widget? child) { - Trip trip = widget.trip!; - log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks"); - - List children; - - if (trip.uuid != 'pending' && trip.uuid != 'error') { - log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks"); - if (trip.landmarks.length <= 1) { - children = [ - const Text("No landmarks in this trip"), - ]; - } else { - children = [ - landmarksWithSteps(), - saveButton(), - ]; - } - } else if(trip.uuid == 'pending') { - // the trip is still being fetched from the api - children = [Center(child: CircularProgressIndicator())]; - } else { - // trip.uuid == 'error' - // show the error raised by the api - // String error = - children = [ - const Icon( - Icons.error_outline, - color: Colors.red, - size: 60, - ), - Padding( - padding: const EdgeInsets.only(top: 16), - child: Text('Error: ${trip.errorDescription}'), - ), - ]; - } - - return Column( - children: children, - ); - }, - ); - } - Widget saveButton() => ElevatedButton( - onPressed: () async { - Trip? trip = await widget.trip; - SharedPreferences prefs = await SharedPreferences.getInstance(); - trip?.toPrefs(prefs); - }, - child: const Text('Save'), - ); - - Widget landmarksWithSteps() { - return ListenableBuilder( - listenable: widget.trip!, - builder: (BuildContext context, Widget? child) { - List children = []; - for (Landmark landmark in widget.trip!.landmarks) { - children.add( - Dismissible( - key: ValueKey(landmark.hashCode), - child: LandmarkCard(landmark), - dismissThresholds: {DismissDirection.endToStart: 0.6}, - onDismissed: (direction) { - // Remove the item from the data source. - log(landmark.name); - setState(() { - widget.trip!.removeLandmark(landmark); - }); - // Then show a snackbar. - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text("We won't show ${landmark.name} again"))); - }, - background: Container(color: Colors.red), - secondaryBackground: Container( - color: Colors.red, - child: Icon( - Icons.delete, - color: Colors.white, - ), - padding: EdgeInsets.all(15), - alignment: Alignment.centerRight, - ), - ) - ); - if (landmark.next != null) { - Widget step = stepBetweenLandmarks(landmark, landmark.next!); - children.add(step); - } - } - return Column( - children: children - ); - }, - ); - } -} - - -Widget stepBetweenLandmarks(Landmark current, Landmark next) { - int timeRounded = 5 * (current.tripTime?.inMinutes ?? 0) ~/ 5; - // ~/ is integer division (rounding) - return Container( - margin: EdgeInsets.all(10), - padding: EdgeInsets.all(10), - decoration: BoxDecoration( - border: Border( - left: BorderSide(width: 3.0, color: Colors.black), - ), - // gradient: LinearGradient( - // begin: Alignment.topLeft, - // end: Alignment.bottomRight, - // colors: [Colors.grey, Colors.white, Colors.white], - // ), - ), - child: Row( - children: [ - Column( - children: [ - Icon(Icons.directions_walk), - Text("~$timeRounded min", style: TextStyle(fontSize: 10)), - ], - ), - Spacer(), - ElevatedButton( - onPressed: () { - // Open navigation instructions - }, - child: Row( - children: [ - Icon(Icons.directions), - Text("Directions"), - ], - ), - ) - ], - ), - ); -} - - - diff --git a/frontend/lib/modules/map.dart b/frontend/lib/modules/map.dart index d47f88f..205eabd 100644 --- a/frontend/lib/modules/map.dart +++ b/frontend/lib/modules/map.dart @@ -1,6 +1,7 @@ import 'dart:collection'; -import 'dart:developer'; +import 'package:anyway/constants.dart'; +import 'package:anyway/modules/themed_marker.dart'; import 'package:flutter/material.dart'; import 'package:anyway/structs/landmark.dart'; import 'package:anyway/structs/trip.dart'; @@ -54,7 +55,7 @@ class _MapWidgetState extends State { Marker marker = Marker( markerId: MarkerId(landmark.uuid), position: LatLng(location[0], location[1]), - icon: await CustomMarker(landmark: landmark, position: i).toBitmapDescriptor( + icon: await ThemedMarker(landmark: landmark, position: i).toBitmapDescriptor( logicalSize: const Size(150, 150), imageSize: const Size(150, 150) ), @@ -75,77 +76,7 @@ class _MapWidgetState extends State { onCameraIdle: _onCameraIdle, // onLongPress: , markers: mapMarkers, - cloudMapId: '41c21ac9b81dbfd8', + cloudMapId: MAP_ID, ); } } - - -class CustomMarker extends StatelessWidget { - final Landmark landmark; - final int position; - - CustomMarker({ - super.key, - required this.landmark, - required this.position - }); - - @override - Widget build(BuildContext context) { - // This returns an outlined circle, with an icon corresponding to the landmark type - // As a small dot, the number of the landmark is displayed in the top right - Icon icon; - if (landmark.type == sightseeing) { - icon = Icon(Icons.church, color: Colors.black, size: 50); - } else if (landmark.type == nature) { - icon = Icon(Icons.park, color: Colors.black, size: 50); - } else if (landmark.type == shopping) { - icon = Icon(Icons.shopping_cart, color: Colors.black, size: 50); - } else if (landmark.type == start || landmark.type == finish) { - icon = Icon(Icons.flag, color: Colors.black, size: 50); - } else { - icon = Icon(Icons.location_on, color: Colors.black, size: 50); - } - - Widget? positionIndicator; - if (landmark.type != start && landmark.type != finish) { - positionIndicator = Positioned( - top: 0, - right: 0, - child: Container( - padding: EdgeInsets.all(5), - decoration: BoxDecoration( - color: Theme.of(context).primaryColor, - shape: BoxShape.circle, - ), - child: Text('$position', style: TextStyle(color: Colors.white, fontSize: 20)), - ), - ); - } - - return RepaintBoundary( - child: Stack( - children: [ - Container( - // these are not the final sizes, since the final size is set in the toBitmapDescriptor method - // they are useful nevertheless to ensure the scale of the components are correct - width: 75, - height: 75, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: Alignment.bottomRight, - colors: [Colors.red, Colors.yellow] - ), - shape: BoxShape.circle, - border: Border.all(color: Colors.black, width: 5), - ), - child: icon, - ), - positionIndicator ?? Container(), - ], - ), - ); - } -} \ No newline at end of file diff --git a/frontend/lib/modules/new_trip_button.dart b/frontend/lib/modules/new_trip_button.dart new file mode 100644 index 0000000..0d1a479 --- /dev/null +++ b/frontend/lib/modules/new_trip_button.dart @@ -0,0 +1,78 @@ +import 'package:anyway/layout.dart'; +import 'package:anyway/structs/preferences.dart'; +import 'package:anyway/structs/trip.dart'; +import 'package:anyway/utils/fetch_trip.dart'; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; + + +class NewTripButton extends StatefulWidget { + final Trip trip; + + const NewTripButton({required this.trip}); + + @override + State createState() => _NewTripButtonState(); +} + +class _NewTripButtonState extends State { + + @override + Widget build(BuildContext context) { + return ListenableBuilder( + listenable: widget.trip, + builder: (BuildContext context, Widget? child) { + if (widget.trip.landmarks.isEmpty){ + return Container(); + } + return SizedBox( + width: 200, + child: ElevatedButton( + onPressed: () async { + Future preferences = loadUserPreferences(); + Trip trip = widget.trip; + fetchTrip(trip, preferences); + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => BasePage(mainScreen: "map", trip: trip) + ) + ); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.add, + ), + Expanded( + child: Padding( + padding: EdgeInsets.only(left: 10, top: 5, bottom: 5, right: 5), + child: FutureBuilder( + future: widget.trip.cityName, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return AutoSizeText( + 'New trip to ${snapshot.data.toString()}', + style: TextStyle(fontSize: 18), + maxLines: 2, + ); + } else { + return AutoSizeText( + 'New trip to ...', + style: TextStyle(fontSize: 18), + maxLines: 2, + ); + } + }, + ) + ) + ) + ], + ), + ), + ); + } + ); + } +} + diff --git a/frontend/lib/modules/new_trip_location_search.dart b/frontend/lib/modules/new_trip_location_search.dart new file mode 100644 index 0000000..088d08f --- /dev/null +++ b/frontend/lib/modules/new_trip_location_search.dart @@ -0,0 +1,64 @@ + +// A search bar that allow the user to enter a city name +import 'package:anyway/structs/landmark.dart'; +import 'package:geocoding/geocoding.dart'; +import 'dart:developer'; + +import 'package:anyway/structs/trip.dart'; +import 'package:flutter/material.dart'; + +class NewTripLocationSearch extends StatefulWidget { + Trip trip; + NewTripLocationSearch( + this.trip, + ); + + + @override + State createState() => _NewTripLocationSearchState(); +} + +class _NewTripLocationSearchState extends State { + final TextEditingController _controller = TextEditingController(); + + setTripLocation (String query) async { + List locations = []; + log('Searching for: $query'); + + try{ + locations = await locationFromAddress(query); + } catch (e) { + log('No results found for: $query : $e'); + } + + if (locations.isNotEmpty) { + Location location = locations.first; + widget.trip.landmarks.clear(); + widget.trip.addLandmark( + Landmark( + uuid: 'pending', + name: query, + location: [location.latitude, location.longitude], + type: start + ) + ); + } + } + + @override + Widget build(BuildContext context) { + return SearchBar( + hintText: 'Enter a city name or long press on the map.', + onSubmitted: setTripLocation, + controller: _controller, + leading: Icon(Icons.search), + trailing: [ElevatedButton( + onPressed: () { + setTripLocation(_controller.text); + }, + child: Text('Search'), + ),] + + ); + } +} \ No newline at end of file diff --git a/frontend/lib/modules/new_trip_map.dart b/frontend/lib/modules/new_trip_map.dart new file mode 100644 index 0000000..d8232e8 --- /dev/null +++ b/frontend/lib/modules/new_trip_map.dart @@ -0,0 +1,86 @@ + +// A map that allows the user to select a location for a new trip. +import 'dart:developer'; + +import 'package:anyway/constants.dart'; +import 'package:anyway/modules/themed_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:widget_to_marker/widget_to_marker.dart'; + + +class NewTripMap extends StatefulWidget { + Trip trip; + NewTripMap( + this.trip, + ); + + @override + State createState() => _NewTripMapState(); +} + +class _NewTripMapState extends State { + final CameraPosition _cameraPosition = CameraPosition( + target: LatLng(48.8566, 2.3522), + zoom: 11.0, + ); + late GoogleMapController _mapController; + final Set _markers = {}; + + _onLongPress(LatLng location) { + log('Long press: $location'); + widget.trip.landmarks.clear(); + widget.trip.addLandmark( + Landmark( + uuid: 'pending', + name: 'start', + location: [location.latitude, location.longitude], + type: start + ) + ); + } + + updateTripDetails() async { + _markers.clear(); + if (widget.trip.landmarks.isNotEmpty) { + Landmark landmark = widget.trip.landmarks.first; + _markers.add( + Marker( + markerId: MarkerId(landmark.uuid), + position: LatLng(landmark.location[0], landmark.location[1]), + icon: await ThemedMarker(landmark: landmark, position: 0).toBitmapDescriptor( + logicalSize: const Size(150, 150), + imageSize: const Size(150, 150) + ), + ) + ); + _mapController.moveCamera( + CameraUpdate.newLatLng( + LatLng(landmark.location[0], landmark.location[1]) + ) + ); + setState(() {}); + } + } + + void _onMapCreated(GoogleMapController controller) async { + _mapController = controller; + } + + + + @override + Widget build(BuildContext context) { + widget.trip.addListener(updateTripDetails); + return GoogleMap( + onMapCreated: _onMapCreated, + initialCameraPosition: _cameraPosition, + onLongPress: _onLongPress, + markers: _markers, + cloudMapId: MAP_ID, + mapToolbarEnabled: false, + ); + } +} \ No newline at end of file diff --git a/frontend/lib/modules/save_button.dart b/frontend/lib/modules/save_button.dart new file mode 100644 index 0000000..d95c580 --- /dev/null +++ b/frontend/lib/modules/save_button.dart @@ -0,0 +1,33 @@ + +import 'package:anyway/structs/trip.dart'; +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +Widget saveButton(Trip trip) => ElevatedButton( + onPressed: () async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + trip.toPrefs(prefs); + }, + child: SizedBox( + width: 100, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.save, + ), + Expanded( + child: Padding( + padding: EdgeInsets.only(left: 10, top: 5, bottom: 5, right: 5), + child: AutoSizeText( + 'Save trip', + maxLines: 2, + ), + ), + ), + ], + ), + ) +); + diff --git a/frontend/lib/modules/step_between_landmarks.dart b/frontend/lib/modules/step_between_landmarks.dart new file mode 100644 index 0000000..4d0f992 --- /dev/null +++ b/frontend/lib/modules/step_between_landmarks.dart @@ -0,0 +1,60 @@ +import 'package:anyway/structs/landmark.dart'; +import 'package:flutter/material.dart'; + +class StepBetweenLandmarks extends StatefulWidget { + final Landmark current; + final Landmark next; + + const StepBetweenLandmarks({ + super.key, + required this.current, + required this.next + }); + + @override + State createState() => _StepBetweenLandmarksState(); +} + +class _StepBetweenLandmarksState extends State { + @override + Widget build(BuildContext context) { + int timeRounded = 5 * (widget.current.tripTime?.inMinutes ?? 0) ~/ 5; + // ~/ is integer division (rounding) + return Container( + margin: EdgeInsets.all(10), + padding: EdgeInsets.all(10), + decoration: BoxDecoration( + border: Border( + left: BorderSide(width: 3.0, color: Colors.black), + ), + // gradient: LinearGradient( + // begin: Alignment.topLeft, + // end: Alignment.bottomRight, + // colors: [Colors.grey, Colors.white, Colors.white], + // ), + ), + child: Row( + children: [ + Column( + children: [ + Icon(Icons.directions_walk), + Text("~$timeRounded min", style: TextStyle(fontSize: 10)), + ], + ), + Spacer(), + ElevatedButton( + onPressed: () { + // Open navigation instructions + }, + child: Row( + children: [ + Icon(Icons.directions), + Text("Directions"), + ], + ), + ) + ], + ), + ); + } +} diff --git a/frontend/lib/modules/themed_marker.dart b/frontend/lib/modules/themed_marker.dart new file mode 100644 index 0000000..c8b7ea2 --- /dev/null +++ b/frontend/lib/modules/themed_marker.dart @@ -0,0 +1,68 @@ +import 'package:anyway/structs/landmark.dart'; +import 'package:flutter/material.dart'; + + +class ThemedMarker extends StatelessWidget { + final Landmark landmark; + final int position; + + ThemedMarker({ + super.key, + required this.landmark, + required this.position + }); + + @override + Widget build(BuildContext context) { + // This returns an outlined circle, with an icon corresponding to the landmark type + // As a small dot, the number of the landmark is displayed in the top right + Icon icon; + if (landmark.type == sightseeing) { + icon = Icon(Icons.church, color: Colors.black, size: 50); + } else if (landmark.type == nature) { + icon = Icon(Icons.park, color: Colors.black, size: 50); + } else if (landmark.type == shopping) { + icon = Icon(Icons.shopping_cart, color: Colors.black, size: 50); + } else if (landmark.type == start || landmark.type == finish) { + icon = Icon(Icons.flag, color: Colors.black, size: 50); + } else { + icon = Icon(Icons.location_on, color: Colors.black, size: 50); + } + + Widget? positionIndicator; + if (landmark.type != start && landmark.type != finish) { + positionIndicator = Positioned( + top: 0, + right: 0, + child: Container( + padding: EdgeInsets.all(5), + decoration: BoxDecoration( + color: Colors.grey[100], + shape: BoxShape.circle, + ), + child: Text('$position', style: TextStyle(color: Colors.black, fontSize: 25)), + ), + ); + } + + return RepaintBoundary( + child: Stack( + alignment: Alignment.topRight, + children: [ + Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [Colors.red, Colors.yellow] + ), + shape: BoxShape.circle, + border: Border.all(color: Colors.black, width: 5), + ), + padding: EdgeInsets.all(5), + child: icon + ), + if (positionIndicator != null) positionIndicator, + ], + ), + ); + } +} \ No newline at end of file diff --git a/frontend/lib/modules/trips_overview.dart b/frontend/lib/modules/trips_saved_list.dart similarity index 100% rename from frontend/lib/modules/trips_overview.dart rename to frontend/lib/modules/trips_saved_list.dart diff --git a/frontend/lib/pages/new_trip.dart b/frontend/lib/pages/new_trip.dart index f3e97d3..78d6cef 100644 --- a/frontend/lib/pages/new_trip.dart +++ b/frontend/lib/pages/new_trip.dart @@ -1,3 +1,4 @@ +import 'package:anyway/modules/new_trip_button.dart'; import 'package:anyway/structs/landmark.dart'; import 'package:flutter/material.dart'; import 'package:geocoding/geocoding.dart'; @@ -6,7 +7,8 @@ import 'package:anyway/layout.dart'; import 'package:anyway/utils/fetch_trip.dart'; import 'package:anyway/structs/preferences.dart'; import "package:anyway/structs/trip.dart"; - +import 'package:anyway/modules/new_trip_location_search.dart'; +import 'package:anyway/modules/new_trip_map.dart'; class NewTripPage extends StatefulWidget { @@ -20,74 +22,31 @@ class _NewTripPageState extends State { final GlobalKey _formKey = GlobalKey(); final TextEditingController latController = TextEditingController(); final TextEditingController lonController = TextEditingController(); + Trip trip = Trip(); @override Widget build(BuildContext context) { + // floating search bar and map as a background return Scaffold( appBar: AppBar( title: const Text('New Trip'), ), - body: Form( - key: _formKey, - child: Padding( - padding: const EdgeInsets.all(15.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - - children: [ - TextFormField( - decoration: const InputDecoration(hintText: 'Lat'), - controller: latController, - validator: (String? value) { - if (value == null || value.isEmpty || double.tryParse(value) == null){ - return 'Please enter a floating point number'; - } - return null; - }, - ), - TextFormField( - decoration: const InputDecoration(hintText: 'Lon'), - controller: lonController, - - validator: (String? value) { - if (value == null || value.isEmpty || double.tryParse(value) == null){ - return 'Please enter a floating point number'; - } - return null; - }, - ), - Divider(height: 15, color: Colors.transparent), - ElevatedButton( - child: const Text('Create trip'), - onPressed: () { - if (_formKey.currentState!.validate()) { - List startPoint = [ - double.parse(latController.text), - double.parse(lonController.text) - ]; - Future preferences = loadUserPreferences(); - Trip trip = Trip(); - trip.landmarks.add( - Landmark( - location: startPoint, - name: "Start", - type: start, - uuid: "pending" - ) - ); - fetchTrip(trip, preferences); - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => BasePage(mainScreen: "map", trip: trip) - ) - ); - } - }, - ), - ], + body: Stack( + children: [ + NewTripMap(trip), + Padding( + padding: EdgeInsets.all(15), + child: NewTripLocationSearch(trip), ), - ) - ) + Align( + alignment: Alignment.bottomRight, + child: Padding( + padding: EdgeInsets.all(15), + child: NewTripButton(trip: trip) + ), + ), + ], + ), ); } } diff --git a/frontend/lib/pages/overview.dart b/frontend/lib/pages/overview.dart deleted file mode 100644 index e98d91b..0000000 --- a/frontend/lib/pages/overview.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:sliding_up_panel/sliding_up_panel.dart'; - -import 'package:anyway/structs/trip.dart'; - -import 'package:anyway/modules/landmarks_overview.dart'; -import 'package:anyway/modules/map.dart'; -import 'package:anyway/modules/greeter.dart'; - - - -class NavigationOverview extends StatefulWidget { - final Trip trip; - - NavigationOverview({ - required this.trip, - }); - - @override - State createState() => _NavigationOverviewState(); -} - - - -class _NavigationOverviewState extends State { - - @override - Widget build(BuildContext context) { - return SlidingUpPanel( - panel: _floatingPanel(), - // collapsed: _floatingCollapsed(), - body: MapWidget(trip: widget.trip), - // renderPanelSheet: false, - // backdropEnabled: true, - maxHeight: MediaQuery.of(context).size.height * 0.8, - padding: EdgeInsets.all(10), - // panelSnapping: false, - borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), - boxShadow: [ - BoxShadow( - blurRadius: 20.0, - color: Colors.black, - ) - ], - ); - } - - Widget _floatingCollapsed(){ - return Greeter( - trip: widget.trip - ); - } - - Widget _floatingPanel(){ - return Column( - children: [ - Padding( - padding: const EdgeInsets.all(15), - child: - Center( - child: Container( - width: 40, - height: 5, - decoration: BoxDecoration( - color: Colors.grey[300], - borderRadius: BorderRadius.all(Radius.circular(12.0)), - ), - ), - ), - ), - Expanded( - child: ListView( - children: [ - Greeter(trip: widget.trip), - LandmarksOverview(trip: widget.trip) - ] - ) - ) - ], - ); - } -} diff --git a/frontend/lib/pages/trip.dart b/frontend/lib/pages/trip.dart new file mode 100644 index 0000000..2d6e58e --- /dev/null +++ b/frontend/lib/pages/trip.dart @@ -0,0 +1,84 @@ +import 'package:anyway/modules/save_button.dart'; +import 'package:flutter/material.dart'; +import 'package:sliding_up_panel/sliding_up_panel.dart'; + +import 'package:anyway/structs/trip.dart'; +import 'package:anyway/modules/landmarks_list.dart'; +import 'package:anyway/modules/greeter.dart'; +import 'package:anyway/modules/map.dart'; + + + +class TripPage extends StatefulWidget { + final Trip trip; + + TripPage({ + required this.trip, + }); + + @override + State createState() => _TripPageState(); +} + + + +class _TripPageState extends State { + + @override + Widget build(BuildContext context) { + return SlidingUpPanel( + panelBuilder: (sc) => _panelFull(sc), + // collapsed: _floatingCollapsed(), + body: MapWidget(trip: widget.trip), + // renderPanelSheet: false, + // backdropEnabled: true, + maxHeight: MediaQuery.of(context).size.height * 0.8, + padding: EdgeInsets.only(left: 10, right: 10, top: 25, bottom: 10), + // panelSnapping: false, + borderRadius: BorderRadius.only(topLeft: Radius.circular(25), topRight: Radius.circular(25)), + boxShadow: [ + BoxShadow( + blurRadius: 20.0, + color: Colors.black, + ) + ], + ); + } + + + Widget _panelFull(ScrollController sc) { + return ListenableBuilder( + listenable: widget.trip, + builder: (context, child) { + if (widget.trip.uuid != 'pending' && widget.trip.uuid != 'error') { + return ListView( + controller: sc, + padding: EdgeInsets.only(bottom: 35), + children: [ + Greeter(trip: widget.trip), + ...landmarksList(widget.trip), + Padding(padding: EdgeInsets.only(top: 10)), + Center(child: saveButton(widget.trip)), + ], + ); + } else if(widget.trip.uuid == 'pending') { + return Greeter(trip: widget.trip); + } else { + return Column( + children: [ + const Icon( + Icons.error_outline, + color: Colors.red, + size: 60, + ), + Padding( + padding: const EdgeInsets.only(top: 16), + child: Text('Error: ${widget.trip.errorDescription}'), + ), + ], + ); + } + } + ); + } +} diff --git a/frontend/lib/structs/trip.dart b/frontend/lib/structs/trip.dart index ec0f228..296ba25 100644 --- a/frontend/lib/structs/trip.dart +++ b/frontend/lib/structs/trip.dart @@ -3,6 +3,7 @@ import 'dart:collection'; import 'dart:convert'; +import 'dart:developer'; import 'package:anyway/structs/landmark.dart'; import 'package:flutter/foundation.dart'; @@ -75,8 +76,11 @@ class Trip with ChangeNotifier { String? content = prefs.getString('trip_$uuid'); Map json = jsonDecode(content!); Trip trip = Trip.fromJson(json); - String? firstUUID = json['entry_uuid']; - readLandmarks(trip.landmarks, prefs, firstUUID); + String? firstUUID = json['first_landmark_uuid']; + log('Loading trip $uuid with first landmark $firstUUID'); + LinkedList landmarks = readLandmarks(prefs, firstUUID); + trip.landmarks = landmarks; + // notifyListeners(); return trip; } @@ -90,6 +94,7 @@ class Trip with ChangeNotifier { void toPrefs(SharedPreferences prefs){ Map json = toJson(); + log('Saving trip $uuid : $json'); prefs.setString('trip_$uuid', jsonEncode(json)); for (Landmark landmark in landmarks) { landmarkToPrefs(prefs, landmark, landmark.next); @@ -99,12 +104,14 @@ class Trip with ChangeNotifier { // Helper -readLandmarks(LinkedList landmarks, SharedPreferences prefs, String? firstUUID) { +LinkedList readLandmarks(SharedPreferences prefs, String? firstUUID) { + LinkedList landmarks = LinkedList(); while (firstUUID != null) { var (head, nextUUID) = getLandmarkFromPrefs(prefs, firstUUID); landmarks.add(head); firstUUID = nextUUID; } + return landmarks; } -- 2.47.2 From 22ca038017bb13a6e8df956f05a548571e2f8fa9 Mon Sep 17 00:00:00 2001 From: Remy Moll Date: Fri, 9 Aug 2024 11:39:11 +0200 Subject: [PATCH 2/4] trip loading (from device storage) much improved --- frontend/lib/layout.dart | 89 ++++-------------- frontend/lib/modules/trips_saved_list.dart | 3 +- frontend/lib/utils/load_trips.dart | 100 --------------------- frontend/pubspec.lock | 24 ++--- 4 files changed, 32 insertions(+), 184 deletions(-) diff --git a/frontend/lib/layout.dart b/frontend/lib/layout.dart index be398c0..5979e0e 100644 --- a/frontend/lib/layout.dart +++ b/frontend/lib/layout.dart @@ -42,7 +42,25 @@ class _BasePageState extends State { if (widget.mainScreen == "map") { - currentView = TripPage(trip: widget.trip ?? getFirstTrip(trips)); + if (widget.trip != null) { + currentView = TripPage(trip: widget.trip!); + } else { + currentView = FutureBuilder( + future: trips, + builder: (context, snapshot) { + if (snapshot.hasData) { + List availableTrips = snapshot.data!; + if (availableTrips.isNotEmpty) { + return TripPage(trip: availableTrips[0]); + } else { + return Text("Wow, so empty!"); + } + } else { + return const Text("loading..."); + } + }, + ); + } } else if (widget.mainScreen == "tutorial") { currentView = TutorialPage(); } else if (widget.mainScreen == "profile") { @@ -134,72 +152,3 @@ class _BasePageState extends State { ); } } - -// This function is used to get the first trip from a list of trips -// TODO: Implement this function -Trip getFirstTrip(Future> trips) { - Trip t1 = Trip(uuid: '1', landmarks: LinkedList()); - t1.landmarks.add( - Landmark( - uuid: '0', - name: "Start", - location: [48.85, 2.32], - type: start, - ), - ); - t1.landmarks.add( - Landmark( - uuid: '1', - name: "Eiffel Tower", - location: [48.859, 2.295], - type: sightseeing, - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg" - ), - ); - t1.landmarks.add( - Landmark( - uuid: "2", - name: "Notre Dame Cathedral", - location: [48.8530, 2.3498], - type: sightseeing, - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Notre-Dame_de_Paris%2C_4_October_2017.jpg/440px-Notre-Dame_de_Paris%2C_4_October_2017.jpg" - ), - ); - t1.landmarks.add( - Landmark( - uuid: "3", - name: "Louvre palace", - location: [48.8606, 2.3376], - type: sightseeing, - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Louvre_Museum_Wikimedia_Commons.jpg/540px-Louvre_Museum_Wikimedia_Commons.jpg" - ), - ); - t1.landmarks.add( - Landmark( - uuid: "4", - name: "Pont-des-arts", - location: [48.8585, 2.3376], - type: sightseeing, - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg/560px-Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg" - ), - ); - t1.landmarks.add( - Landmark( - uuid: "5", - name: "Panthéon", - location: [48.847, 2.347], - type: sightseeing, - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Pantheon_of_Paris_007.JPG/1280px-Pantheon_of_Paris_007.JPG" - ), - ); - t1.landmarks.add( - Landmark( - uuid: "6", - name: "Galeries Lafayette", - location: [48.87, 2.32], - type: shopping, - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/de/GaleriesLafayetteNuit.jpg/220px-GaleriesLafayetteNuit.jpg" - ), - ); - return t1; -} \ No newline at end of file diff --git a/frontend/lib/modules/trips_saved_list.dart b/frontend/lib/modules/trips_saved_list.dart index 765ef0e..08fce0a 100644 --- a/frontend/lib/modules/trips_saved_list.dart +++ b/frontend/lib/modules/trips_saved_list.dart @@ -16,8 +16,6 @@ class TripsOverview extends StatefulWidget { } class _TripsOverviewState extends State { - // final Future> _trips = loadTrips(); - Widget listBuild (BuildContext context, AsyncSnapshot> snapshot) { List children; @@ -65,6 +63,7 @@ class _TripsOverviewState extends State { return ListView( children: children, + padding: const EdgeInsets.only(top: 0), ); } diff --git a/frontend/lib/utils/load_trips.dart b/frontend/lib/utils/load_trips.dart index f608294..cd4bdd7 100644 --- a/frontend/lib/utils/load_trips.dart +++ b/frontend/lib/utils/load_trips.dart @@ -15,105 +15,5 @@ Future> loadTrips() async { trips.add(Trip.fromPrefs(prefs, uuid)); } } - - if (trips.isEmpty) { - Trip t1 = Trip(uuid: '1', landmarks: LinkedList()); - t1.landmarks.add( - Landmark( - uuid: '1', - name: "Eiffel Tower", - location: [48.859, 2.295], - type: LandmarkType(name: "Tower"), - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg" - ), - ); - t1.landmarks.add( - Landmark( - uuid: "2", - name: "Notre Dame Cathedral", - location: [48.8530, 2.3498], - type: LandmarkType(name: "Monument"), - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Notre-Dame_de_Paris%2C_4_October_2017.jpg/440px-Notre-Dame_de_Paris%2C_4_October_2017.jpg" - ), - ); - t1.landmarks.add( - Landmark( - uuid: "3", - name: "Louvre palace", - location: [48.8606, 2.3376], - type: LandmarkType(name: "Museum"), - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Louvre_Museum_Wikimedia_Commons.jpg/540px-Louvre_Museum_Wikimedia_Commons.jpg" - ), - ); - t1.landmarks.add( - Landmark( - uuid: "4", - name: "Pont-des-arts", - location: [48.8585, 2.3376], - type: LandmarkType(name: "Bridge"), - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg/560px-Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg" - ), - ); - t1.landmarks.add( - Landmark( - uuid: "5", - name: "Panthéon", - location: [48.847, 2.347], - type: LandmarkType(name: "Monument"), - imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Pantheon_of_Paris_007.JPG/1280px-Pantheon_of_Paris_007.JPG" - ), - ); - trips.add(t1); - - - Trip t2 = Trip(uuid: '2', landmarks: LinkedList()); - - t2.landmarks.add( - Landmark( - uuid: '21', - name: "St. Charles's Church", - location: [48.1924563,16.3334399], - type: LandmarkType(name: "Monument"), - imageURL: "https://lh5.googleusercontent.com/p/AF1QipNNmA76Ps71NCL9rOOFoyheCEOyXWdHcUgQx9jd=w408-h305-k-no" - ), - ); - t2.landmarks.add( - Landmark( - uuid: "22", - name: "Vienna State Opera", - location: [48.1949124,16.3483292], - type: LandmarkType(name: "Culture"), - imageURL: "https://lh5.googleusercontent.com/p/AF1QipMOx398kcoeDXFruSHNsb4lmZtdT8vibtK0cLi-=w408-h306-k-no" - ), - ); - t2.landmarks.add( - Landmark( - uuid: "23", - name: "Belvedere-Schlossgarten", - location: [48.1956427,16.3711521], - type: LandmarkType(name: "Nature"), - imageURL: "https://lh5.googleusercontent.com/p/AF1QipNcI5LImH2Qdzx0GmF-5CY1wRKINFZ7HkahPEy1=w408-h306-k-no" - ), - ); - t2.landmarks.add( - Landmark( - uuid: "24", - name: "Kunsthistorisches Museum Wien", - location: [48.2047501,16.3581904], - type: LandmarkType(name: "Museum"), - imageURL: "https://lh5.googleusercontent.com/p/AF1QipPuDu-kCCowO4TcawjziE8AhDVAANagVtRYBjlv=w408-h450-k-no" - ), - ); - t2.landmarks.add( - Landmark( - uuid: "25", - name: "Salztorbrücke", - location: [48.2132382,16.369051], - type: LandmarkType(name: "Bridge"), - ), - ); - trips.add(t2); - - } return trips; } diff --git a/frontend/pubspec.lock b/frontend/pubspec.lock index 6f808e0..bbc1165 100644 --- a/frontend/pubspec.lock +++ b/frontend/pubspec.lock @@ -292,18 +292,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -332,18 +332,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" nested: dependency: transitive description: @@ -609,10 +609,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" typed_data: dependency: transitive description: @@ -641,10 +641,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.4" web: dependency: transitive description: -- 2.47.2 From 6d3399640e752a3d255006e976e5adace5f15172 Mon Sep 17 00:00:00 2001 From: Remy Moll Date: Sat, 10 Aug 2024 17:14:56 +0200 Subject: [PATCH 3/4] use more fitting floating action button, cleanup --- frontend/lib/layout.dart | 2 +- ...greeter.dart => current_trip_greeter.dart} | 0 ....dart => current_trip_landmarks_list.dart} | 0 .../{map.dart => current_trip_map.dart} | 3 + ...ton.dart => current_trip_save_button.dart} | 0 frontend/lib/modules/new_trip_button.dart | 74 ++++++++----------- frontend/lib/modules/new_trip_map.dart | 1 + .../pages/{trip.dart => current_trip.dart} | 8 +- frontend/lib/pages/new_trip.dart | 8 +- 9 files changed, 40 insertions(+), 56 deletions(-) rename frontend/lib/modules/{greeter.dart => current_trip_greeter.dart} (100%) rename frontend/lib/modules/{landmarks_list.dart => current_trip_landmarks_list.dart} (100%) rename frontend/lib/modules/{map.dart => current_trip_map.dart} (97%) rename frontend/lib/modules/{save_button.dart => current_trip_save_button.dart} (100%) rename frontend/lib/pages/{trip.dart => current_trip.dart} (89%) diff --git a/frontend/lib/layout.dart b/frontend/lib/layout.dart index 5979e0e..1369ca2 100644 --- a/frontend/lib/layout.dart +++ b/frontend/lib/layout.dart @@ -11,7 +11,7 @@ import 'package:anyway/utils/load_trips.dart'; import 'package:anyway/pages/new_trip.dart'; import 'package:anyway/pages/tutorial.dart'; -import 'package:anyway/pages/trip.dart'; +import 'package:anyway/pages/current_trip.dart'; import 'package:anyway/pages/profile.dart'; diff --git a/frontend/lib/modules/greeter.dart b/frontend/lib/modules/current_trip_greeter.dart similarity index 100% rename from frontend/lib/modules/greeter.dart rename to frontend/lib/modules/current_trip_greeter.dart diff --git a/frontend/lib/modules/landmarks_list.dart b/frontend/lib/modules/current_trip_landmarks_list.dart similarity index 100% rename from frontend/lib/modules/landmarks_list.dart rename to frontend/lib/modules/current_trip_landmarks_list.dart diff --git a/frontend/lib/modules/map.dart b/frontend/lib/modules/current_trip_map.dart similarity index 97% rename from frontend/lib/modules/map.dart rename to frontend/lib/modules/current_trip_map.dart index 205eabd..dfd174a 100644 --- a/frontend/lib/modules/map.dart +++ b/frontend/lib/modules/current_trip_map.dart @@ -77,6 +77,9 @@ class _MapWidgetState extends State { // onLongPress: , markers: mapMarkers, cloudMapId: MAP_ID, + mapToolbarEnabled: false, + zoomControlsEnabled: false, + ); } } diff --git a/frontend/lib/modules/save_button.dart b/frontend/lib/modules/current_trip_save_button.dart similarity index 100% rename from frontend/lib/modules/save_button.dart rename to frontend/lib/modules/current_trip_save_button.dart diff --git a/frontend/lib/modules/new_trip_button.dart b/frontend/lib/modules/new_trip_button.dart index 0d1a479..99491f2 100644 --- a/frontend/lib/modules/new_trip_button.dart +++ b/frontend/lib/modules/new_trip_button.dart @@ -25,52 +25,38 @@ class _NewTripButtonState extends State { if (widget.trip.landmarks.isEmpty){ return Container(); } - return SizedBox( - width: 200, - child: ElevatedButton( - onPressed: () async { - Future preferences = loadUserPreferences(); - Trip trip = widget.trip; - fetchTrip(trip, preferences); - Navigator.of(context).push( - MaterialPageRoute( - builder: (context) => BasePage(mainScreen: "map", trip: trip) - ) - ); + return FloatingActionButton.extended( + onPressed: () async { + Future preferences = loadUserPreferences(); + Trip trip = widget.trip; + fetchTrip(trip, preferences); + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => BasePage(mainScreen: "map", trip: trip) + ) + ); + }, + icon: Icon(Icons.add), + label: FutureBuilder( + future: widget.trip.cityName, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return AutoSizeText( + 'New trip to ${snapshot.data.toString()}', + style: TextStyle(fontSize: 18), + maxLines: 2, + ); + } else { + return AutoSizeText( + 'New trip to ...', + style: TextStyle(fontSize: 18), + maxLines: 2, + ); + } }, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.add, - ), - Expanded( - child: Padding( - padding: EdgeInsets.only(left: 10, top: 5, bottom: 5, right: 5), - child: FutureBuilder( - future: widget.trip.cityName, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return AutoSizeText( - 'New trip to ${snapshot.data.toString()}', - style: TextStyle(fontSize: 18), - maxLines: 2, - ); - } else { - return AutoSizeText( - 'New trip to ...', - style: TextStyle(fontSize: 18), - maxLines: 2, - ); - } - }, - ) - ) - ) - ], - ), - ), + ) ); + } ); } diff --git a/frontend/lib/modules/new_trip_map.dart b/frontend/lib/modules/new_trip_map.dart index d8232e8..fc0a8c2 100644 --- a/frontend/lib/modules/new_trip_map.dart +++ b/frontend/lib/modules/new_trip_map.dart @@ -81,6 +81,7 @@ class _NewTripMapState extends State { markers: _markers, cloudMapId: MAP_ID, mapToolbarEnabled: false, + zoomControlsEnabled: false, ); } } \ No newline at end of file diff --git a/frontend/lib/pages/trip.dart b/frontend/lib/pages/current_trip.dart similarity index 89% rename from frontend/lib/pages/trip.dart rename to frontend/lib/pages/current_trip.dart index 2d6e58e..793f22f 100644 --- a/frontend/lib/pages/trip.dart +++ b/frontend/lib/pages/current_trip.dart @@ -1,11 +1,11 @@ -import 'package:anyway/modules/save_button.dart'; +import 'package:anyway/modules/current_trip_save_button.dart'; import 'package:flutter/material.dart'; import 'package:sliding_up_panel/sliding_up_panel.dart'; import 'package:anyway/structs/trip.dart'; -import 'package:anyway/modules/landmarks_list.dart'; -import 'package:anyway/modules/greeter.dart'; -import 'package:anyway/modules/map.dart'; +import 'package:anyway/modules/current_trip_landmarks_list.dart'; +import 'package:anyway/modules/current_trip_greeter.dart'; +import 'package:anyway/modules/current_trip_map.dart'; diff --git a/frontend/lib/pages/new_trip.dart b/frontend/lib/pages/new_trip.dart index 78d6cef..741b4dc 100644 --- a/frontend/lib/pages/new_trip.dart +++ b/frontend/lib/pages/new_trip.dart @@ -38,15 +38,9 @@ class _NewTripPageState extends State { padding: EdgeInsets.all(15), child: NewTripLocationSearch(trip), ), - Align( - alignment: Alignment.bottomRight, - child: Padding( - padding: EdgeInsets.all(15), - child: NewTripButton(trip: trip) - ), - ), ], ), + floatingActionButton: NewTripButton(trip: trip), ); } } -- 2.47.2 From d24bc2470b236dcf16bd58d7c1d7381e9b3dde96 Mon Sep 17 00:00:00 2001 From: Remy Moll Date: Sun, 11 Aug 2024 16:06:20 +0200 Subject: [PATCH 4/4] navigation intent gets opened correctly --- frontend/lib/modules/map_chooser.dart | 52 ++++++++++++++ .../lib/modules/step_between_landmarks.dart | 12 ++-- frontend/pubspec.lock | 72 +++++++++++++++++++ frontend/pubspec.yaml | 2 + 4 files changed, 130 insertions(+), 8 deletions(-) create mode 100644 frontend/lib/modules/map_chooser.dart diff --git a/frontend/lib/modules/map_chooser.dart b/frontend/lib/modules/map_chooser.dart new file mode 100644 index 0000000..d9ffc1e --- /dev/null +++ b/frontend/lib/modules/map_chooser.dart @@ -0,0 +1,52 @@ +import 'package:anyway/structs/landmark.dart'; +import 'package:flutter/material.dart'; +import 'package:map_launcher/map_launcher.dart'; +import 'package:flutter_svg/flutter_svg.dart'; + +showMapChooser(BuildContext context, Landmark current, Landmark next) async { + List availableMaps = []; + try { + availableMaps = await MapLauncher.installedMaps; + } catch (e) { + print(e); + } + if (availableMaps.isEmpty) { + return; + } + + showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return SafeArea( + child: SingleChildScrollView( + child: Container( + child: Wrap( + children: [ + for (var map in availableMaps) + ListTile( + onTap: () => map.showDirections( + origin: Coords(current.location[0], current.location[1]), + originTitle: current.name, + destination: Coords(next.location[0], next.location[1]), + destinationTitle: current.name, + directionsMode: DirectionsMode.walking + ), + title: Text(map.mapName), + // rounded corners + leading: ClipRRect( + borderRadius: BorderRadius.circular(8.0), + child: SvgPicture.asset( + map.icon, + height: 30.0, + width: 30.0, + ), + ) + ), + ], + ), + ), + ), + ); + }, + ); +} diff --git a/frontend/lib/modules/step_between_landmarks.dart b/frontend/lib/modules/step_between_landmarks.dart index 4d0f992..58fa106 100644 --- a/frontend/lib/modules/step_between_landmarks.dart +++ b/frontend/lib/modules/step_between_landmarks.dart @@ -1,5 +1,6 @@ import 'package:anyway/structs/landmark.dart'; import 'package:flutter/material.dart'; +import 'package:anyway/modules/map_chooser.dart'; class StepBetweenLandmarks extends StatefulWidget { final Landmark current; @@ -18,7 +19,7 @@ class StepBetweenLandmarks extends StatefulWidget { class _StepBetweenLandmarksState extends State { @override Widget build(BuildContext context) { - int timeRounded = 5 * (widget.current.tripTime?.inMinutes ?? 0) ~/ 5; + int timeRounded = 5 * ((widget.current.tripTime?.inMinutes ?? 0) ~/ 5); // ~/ is integer division (rounding) return Container( margin: EdgeInsets.all(10), @@ -27,11 +28,6 @@ class _StepBetweenLandmarksState extends State { border: Border( left: BorderSide(width: 3.0, color: Colors.black), ), - // gradient: LinearGradient( - // begin: Alignment.topLeft, - // end: Alignment.bottomRight, - // colors: [Colors.grey, Colors.white, Colors.white], - // ), ), child: Row( children: [ @@ -43,8 +39,8 @@ class _StepBetweenLandmarksState extends State { ), Spacer(), ElevatedButton( - onPressed: () { - // Open navigation instructions + onPressed: () async { + showMapChooser(context, widget.current, widget.next); }, child: Row( children: [ diff --git a/frontend/pubspec.lock b/frontend/pubspec.lock index bbc1165..1c7e0ec 100644 --- a/frontend/pubspec.lock +++ b/frontend/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + url: "https://pub.dev" + source: hosted + version: "2.5.0" async: dependency: transitive description: @@ -174,6 +182,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.21" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" + url: "https://pub.dev" + source: hosted + version: "2.0.10+1" flutter_test: dependency: "direct dev" description: flutter @@ -320,6 +336,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.0" + map_launcher: + dependency: "direct main" + description: + name: map_launcher + sha256: af59b9f79f641022e06761c9d4217c6c57b9ef9020af2fdb23155ec87af79e61 + url: "https://pub.dev" + source: hosted + version: "3.3.1" matcher: dependency: transitive description: @@ -368,6 +392,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf + url: "https://pub.dev" + source: hosted + version: "1.0.1" path_provider: dependency: transitive description: @@ -416,6 +448,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" platform: dependency: transitive description: @@ -629,6 +669,30 @@ packages: url: "https://pub.dev" source: hosted version: "4.4.2" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" + url: "https://pub.dev" + source: hosted + version: "1.1.11+1" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da + url: "https://pub.dev" + source: hosted + version: "1.1.11+1" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" + url: "https://pub.dev" + source: hosted + version: "1.1.11+1" vector_math: dependency: transitive description: @@ -669,6 +733,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" sdks: dart: ">=3.4.0 <4.0.0" flutter: ">=3.22.0" diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index bb46eb7..67f35a5 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -45,6 +45,8 @@ dependencies: widget_to_marker: ^1.0.6 provider: ^6.1.2 auto_size_text: ^3.0.0 + map_launcher: ^3.3.1 + flutter_svg: ^2.0.10+1 dev_dependencies: flutter_test: -- 2.47.2