From b1fd0f47fa13e7ddaca03d51bd91aa233bccc182 Mon Sep 17 00:00:00 2001 From: Remy Moll Date: Sat, 3 Aug 2024 16:52:29 +0200 Subject: [PATCH] overhaul using a trip struct that notifies its ui dependencies --- frontend/lib/layout.dart | 68 +++++++++++--- frontend/lib/main.dart | 2 +- frontend/lib/modules/greeter.dart | 95 +++++++++++--------- frontend/lib/modules/landmark_card.dart | 8 +- frontend/lib/modules/landmarks_overview.dart | 67 +++++++------- frontend/lib/modules/map.dart | 8 +- frontend/lib/modules/trips_overview.dart | 2 +- frontend/lib/pages/new_trip.dart | 17 +++- frontend/lib/pages/overview.dart | 81 +++++++++-------- frontend/lib/structs/trip.dart | 51 +++++++---- frontend/lib/utils/fetch_trip.dart | 17 ++-- frontend/lib/utils/load_trips.dart | 4 +- frontend/pubspec.lock | 64 +++++++++++++ frontend/pubspec.yaml | 2 + 14 files changed, 323 insertions(+), 163 deletions(-) diff --git a/frontend/lib/layout.dart b/frontend/lib/layout.dart index abc8b24..fbfc6ca 100644 --- a/frontend/lib/layout.dart +++ b/frontend/lib/layout.dart @@ -1,3 +1,6 @@ +import 'dart:collection'; + +import 'package:anyway/structs/landmark.dart'; import 'package:flutter/material.dart'; import 'package:anyway/constants.dart'; @@ -15,12 +18,12 @@ import 'package:anyway/pages/profile.dart'; // A side drawer is used to switch between pages class BasePage extends StatefulWidget { final String mainScreen; - final Future? trip; - + final Trip? trip; + const BasePage({ super.key, required this.mainScreen, - this.trip + this.trip, }); @override @@ -53,13 +56,13 @@ class _BasePageState extends State { children: [ DrawerHeader( decoration: BoxDecoration( - gradient: LinearGradient(colors: [Colors.cyan, theme.primaryColor]) + gradient: LinearGradient(colors: [Colors.red, Colors.yellow]) ), child: Center( child: Text( APP_NAME, style: TextStyle( - color: Colors.white, + color: Colors.grey[800], fontSize: 24, fontWeight: FontWeight.bold, ), @@ -129,9 +132,54 @@ class _BasePageState extends State { } } - - -Future getFirstTrip (Future> trips) async { - List tripsf = await trips; - return tripsf[0]; +// 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: '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" + ), + ); + return t1; } \ No newline at end of file diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index 7fbb6f8..44c3922 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -13,7 +13,7 @@ class App extends StatelessWidget { return MaterialApp( title: APP_NAME, home: BasePage(mainScreen: "map"), - theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.green), + theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.red[600]), ); } } diff --git a/frontend/lib/modules/greeter.dart b/frontend/lib/modules/greeter.dart index 77e55de..6b74c13 100644 --- a/frontend/lib/modules/greeter.dart +++ b/frontend/lib/modules/greeter.dart @@ -3,12 +3,10 @@ import 'package:anyway/structs/trip.dart'; import 'package:flutter/material.dart'; class Greeter extends StatefulWidget { - final Future trip; - final bool standalone; + final Trip trip; Greeter({ - required this.standalone, - required this.trip + required this.trip, }); @override @@ -18,55 +16,66 @@ class Greeter extends StatefulWidget { class _GreeterState extends State { - Widget greeterBuild (BuildContext context, AsyncSnapshot snapshot) { + Widget greeterBuilder (BuildContext context, Widget? child) { ThemeData theme = Theme.of(context); Widget topGreeter; - if (snapshot.hasData) { - topGreeter = Padding( - padding: const EdgeInsets.only(top: 20, bottom: 20), - child: Text( - 'Welcome to ${snapshot.data?.cityName}!', - style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24), - ) - ); - } else if (snapshot.hasError) { - topGreeter = const Padding( - padding: EdgeInsets.only(top: 20, bottom: 20), - child: Text('Error while fetching trip') + if (widget.trip.landmarks.length > 1) { + topGreeter = FutureBuilder( + future: widget.trip.cityName, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return Text( + 'Welcome to ${snapshot.data}!', + style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24), + ); + } else if (snapshot.hasError) { + return const Text('Welcome to your trip!'); + } else { + return const Text('Welcome to ...'); + } + } ); } else { - // still awaiting the cityname + // still awaiting the trip + // We can hopefully infer the city name from the cityName future // Show a linear loader at the bottom and an info message above topGreeter = Column( mainAxisAlignment: MainAxisAlignment.end, children: [ - Padding( - padding: const EdgeInsets.only(top: 20, bottom: 20), - child: const Text('Generating your trip...', style: TextStyle(fontSize: 20),) + FutureBuilder( + future: widget.trip.cityName, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return Text( + 'Generating your trip to ${snapshot.data}...', + style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24), + ); + } else if (snapshot.hasError) { + return const Text('Error while fetching city name'); + } + return const Text('Generating your trip...'); + } ), - const LinearProgressIndicator() + Padding( + padding: EdgeInsets.all(5), + child: const LinearProgressIndicator() + ) ] ); } - - - if (widget.standalone) { - return Center( - child: topGreeter, - ); - } else { - return Center( - child: Column( - children: [ - Padding(padding: EdgeInsets.only(top: 24.0)), - topGreeter, - bottomGreeter, - Padding(padding: EdgeInsets.only(bottom: 24.0)), - ], - ) - ); - } + return Center( + child: Column( + children: [ + // Padding(padding: EdgeInsets.only(top: 20)), + topGreeter, + Padding( + padding: EdgeInsets.all(20), + child: bottomGreeter + ), + ], + ) + ); } Widget bottomGreeter = const Text( @@ -79,9 +88,9 @@ class _GreeterState extends State { @override Widget build(BuildContext context) { - return FutureBuilder( - future: widget.trip, - builder: greeterBuild, + return ListenableBuilder( + listenable: widget.trip, + builder: greeterBuilder, ); } } \ No newline at end of file diff --git a/frontend/lib/modules/landmark_card.dart b/frontend/lib/modules/landmark_card.dart index ca663b6..579ebca 100644 --- a/frontend/lib/modules/landmark_card.dart +++ b/frontend/lib/modules/landmark_card.dart @@ -1,4 +1,5 @@ import 'package:anyway/structs/landmark.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; @@ -31,9 +32,10 @@ class _LandmarkCardState extends State { height: double.infinity, // force a fixed width width: 160, - child: Image.network( - widget.landmark.imageURL ?? '', - errorBuilder: (context, error, stackTrace) => Icon(Icons.question_mark_outlined), + child: CachedNetworkImage( + imageUrl: widget.landmark.imageURL ?? '', + placeholder: (context, url) => CircularProgressIndicator(), + errorWidget: (context, error, stackTrace) => Icon(Icons.question_mark_outlined), // TODO: make this a switch statement to load a placeholder if null // cover the whole container meaning the image will be cropped fit: BoxFit.cover, diff --git a/frontend/lib/modules/landmarks_overview.dart b/frontend/lib/modules/landmarks_overview.dart index 720fe49..387bf11 100644 --- a/frontend/lib/modules/landmarks_overview.dart +++ b/frontend/lib/modules/landmarks_overview.dart @@ -1,17 +1,17 @@ import 'dart:collection'; +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'; -import 'package:flutter/material.dart'; -import 'package:shared_preferences/shared_preferences.dart'; class LandmarksOverview extends StatefulWidget { - final Future? trip; + final Trip? trip; const LandmarksOverview({super.key, this.trip}); @override @@ -23,18 +23,17 @@ class _LandmarksOverviewState extends State { @override Widget build(BuildContext context) { - final Future> _landmarks = getLandmarks(widget.trip); - return DefaultTextStyle( - style: Theme.of(context).textTheme.displayMedium!, - textAlign: TextAlign.center, - child: FutureBuilder>( - future: _landmarks, - builder: (BuildContext context, AsyncSnapshot> snapshot) { - List children; - if (snapshot.hasData) { - children = [landmarksWithSteps(snapshot.data!), saveButton()]; - } else if (snapshot.hasError) { - children = [ + 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') { + // the trip is still being fetched from the api + children = [Center(child: CircularProgressIndicator())]; + } else if (trip.uuid == 'error') { + children = [ const Icon( Icons.error_outline, color: Colors.red, @@ -42,20 +41,25 @@ class _LandmarksOverviewState extends State { ), Padding( padding: const EdgeInsets.only(top: 16), - child: Text('Error: ${snapshot.error}', style: TextStyle(fontSize: 12)), + child: Text('Error: ${trip.cityName}'), ), ]; + } else { + if (trip.landmarks.length <= 1) { + children = [ + const Text("No landmarks in this trip"), + ]; } else { - children = [Center(child: CircularProgressIndicator())]; + children = [ + landmarksWithSteps(trip.landmarks), + saveButton(), + ]; } - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: children, - ), - ); - }, - ), + } + return Column( + children: children, + ); + }, ); } Widget saveButton() => ElevatedButton( @@ -100,7 +104,7 @@ Widget landmarksWithSteps(LinkedList landmarks) { ); lkey++; if (landmark.next != null) { - Widget step = stepBetweenLandmarks(landmark, landmark.next!); + Widget step = stepBetweenLandmarks(landmark); children.add(step); } } @@ -111,7 +115,7 @@ Widget landmarksWithSteps(LinkedList landmarks) { } -Widget stepBetweenLandmarks(Landmark before, Landmark after) { +Widget stepBetweenLandmarks(Landmark landmark) { // This is a simple widget that draws a line between landmark-cards // It's a vertical dotted line // Next to the line is the icon for the mode of transport (walking for now) and the estimated time @@ -134,7 +138,7 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) { Column( children: [ Icon(Icons.directions_walk), - Text("5 min", style: TextStyle(fontSize: 10)), + Text("${landmark.tripTime} min", style: TextStyle(fontSize: 10)), ], ), Spacer(), @@ -149,8 +153,5 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) { ); } -Future> getLandmarks (Future? trip) async { - Trip tripf = await trip!; - return tripf.landmarks; -} + diff --git a/frontend/lib/modules/map.dart b/frontend/lib/modules/map.dart index d0ab511..2e95ccd 100644 --- a/frontend/lib/modules/map.dart +++ b/frontend/lib/modules/map.dart @@ -8,7 +8,7 @@ import 'package:the_widget_marker/the_widget_marker.dart'; class MapWidget extends StatefulWidget { - final Future? trip; + final Trip? trip; MapWidget({ this.trip @@ -31,8 +31,7 @@ class _MapWidgetState extends State { void _onMapCreated(GoogleMapController controller) async { mapController = controller; - Trip? trip = await widget.trip; - List? newLocation = trip?.landmarks.first.location; + List? newLocation = widget.trip?.landmarks.first.location; if (newLocation != null) { CameraUpdate update = CameraUpdate.newLatLng(LatLng(newLocation[0], newLocation[1])); controller.moveCamera(update); @@ -48,8 +47,7 @@ class _MapWidgetState extends State { void drawLandmarks() async { // (re)draws landmarks on the map - Trip? trip = await widget.trip; - LinkedList? landmarks = trip?.landmarks; + LinkedList? landmarks = widget.trip?.landmarks; if (landmarks != null){ for (Landmark landmark in landmarks) { markers.add(Marker( diff --git a/frontend/lib/modules/trips_overview.dart b/frontend/lib/modules/trips_overview.dart index 8501013..fec7f2a 100644 --- a/frontend/lib/modules/trips_overview.dart +++ b/frontend/lib/modules/trips_overview.dart @@ -30,7 +30,7 @@ class _TripsOverviewState extends State { onTap: () { Navigator.of(context).push( MaterialPageRoute( - builder: (context) => BasePage(mainScreen: "map", trip: Future.value(trip)) + builder: (context) => BasePage(mainScreen: "map", trip: trip) ) ); }, diff --git a/frontend/lib/pages/new_trip.dart b/frontend/lib/pages/new_trip.dart index 2464177..79c15ae 100644 --- a/frontend/lib/pages/new_trip.dart +++ b/frontend/lib/pages/new_trip.dart @@ -1,8 +1,10 @@ +import 'package:anyway/structs/landmark.dart'; +import 'package:flutter/material.dart'; +import 'package:geocoding/geocoding.dart'; import 'package:anyway/layout.dart'; -import 'package:anyway/structs/preferences.dart'; import 'package:anyway/utils/fetch_trip.dart'; -import 'package:flutter/material.dart'; +import 'package:anyway/structs/preferences.dart'; import "package:anyway/structs/trip.dart"; @@ -64,7 +66,16 @@ class _NewTripPageState extends State { double.parse(lonController.text) ]; Future preferences = loadUserPreferences(); - Future? trip = fetchTrip(startPoint, preferences); + Trip trip = Trip(); + trip.landmarks.add( + Landmark( + location: startPoint, + name: "start", + type: LandmarkType(name: 'start'), + uuid: "pending" + ) + ); + fetchTrip(trip, preferences); Navigator.of(context).push( MaterialPageRoute( builder: (context) => BasePage(mainScreen: "map", trip: trip) diff --git a/frontend/lib/pages/overview.dart b/frontend/lib/pages/overview.dart index ce3646b..e98d91b 100644 --- a/frontend/lib/pages/overview.dart +++ b/frontend/lib/pages/overview.dart @@ -10,10 +10,10 @@ import 'package:anyway/modules/greeter.dart'; class NavigationOverview extends StatefulWidget { - final Future trip; + final Trip trip; NavigationOverview({ - required this.trip + required this.trip, }); @override @@ -27,53 +27,56 @@ class _NavigationOverviewState extends State { @override Widget build(BuildContext context) { return SlidingUpPanel( - renderPanelSheet: false, panel: _floatingPanel(), - collapsed: _floatingCollapsed(), - body: MapWidget(trip: widget.trip) + // 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(){ - final ThemeData theme = Theme.of(context); - return Container( - decoration: BoxDecoration( - color: theme.canvasColor, - borderRadius: BorderRadius.only(topLeft: Radius.circular(24.0), topRight: Radius.circular(24.0)), - boxShadow: [] - ), - - child: Greeter(standalone: true, trip: widget.trip) + return Greeter( + trip: widget.trip ); } Widget _floatingPanel(){ - final ThemeData theme = Theme.of(context); - return Container( - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(24.0)), - boxShadow: [ - BoxShadow( - blurRadius: 20.0, - color: theme.shadowColor, - ), - ] - ), - child: Center( - child: Padding( - padding: EdgeInsets.all(8.0), - child: SingleChildScrollView( - child: Column( - children: [ - Greeter(standalone: false, trip: widget.trip), - LandmarksOverview(trip: widget.trip), - ], - ), + 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/structs/trip.dart b/frontend/lib/structs/trip.dart index f6d94e3..3b8c1ff 100644 --- a/frontend/lib/structs/trip.dart +++ b/frontend/lib/structs/trip.dart @@ -5,35 +5,56 @@ import 'dart:collection'; import 'dart:convert'; import 'package:anyway/structs/landmark.dart'; +import 'package:flutter/foundation.dart'; +import 'package:geocoding/geocoding.dart'; import 'package:shared_preferences/shared_preferences.dart'; -class Trip { - final String uuid; - final String cityName; - // TODO: cityName should be inferred from coordinates of the Landmarks - final int totalTime; - final LinkedList landmarks; +class Trip with ChangeNotifier { + String uuid; + int totalTime; + LinkedList landmarks; // could be empty as well + Future get cityName async { + if (GeocodingPlatform.instance == null) { + return '${landmarks.first.location[0]}, ${landmarks.first.location[1]}'; + } + List placemarks = await placemarkFromCoordinates(landmarks.first.location[0], landmarks.first.location[1]); + return placemarks.first.locality ?? 'Unknown'; + } + Trip({ - required this.uuid, - required this.cityName, - required this.landmarks, - this.totalTime = 0 - }); - + this.uuid = 'pending', + this.totalTime = 0, + LinkedList? landmarks + // a trip can be created with no landmarks, but the list should be initialized anyway + }) : landmarks = landmarks ?? LinkedList(); + factory Trip.fromJson(Map json) { Trip trip = Trip( uuid: json['uuid'], - cityName: json['city_name'] ?? 'Not communicated', - landmarks: LinkedList() + totalTime: json['total_time'], ); return trip; } + void loadFromJson(Map json) { + uuid = json['uuid']; + totalTime = json['total_time']; + notifyListeners(); + } + void addLandmark(Landmark landmark) { + landmarks.add(landmark); + notifyListeners(); + } + + void updateUUID(String newUUID) { + uuid = newUUID; + notifyListeners(); + } factory Trip.fromPrefs(SharedPreferences prefs, String uuid) { String? content = prefs.getString('trip_$uuid'); @@ -47,7 +68,7 @@ class Trip { Map toJson() => { 'uuid': uuid, - 'city_name': cityName, + 'total_time': totalTime, 'first_landmark_uuid': landmarks.first.uuid }; diff --git a/frontend/lib/utils/fetch_trip.dart b/frontend/lib/utils/fetch_trip.dart index aeba3b8..54d9365 100644 --- a/frontend/lib/utils/fetch_trip.dart +++ b/frontend/lib/utils/fetch_trip.dart @@ -1,7 +1,7 @@ import "dart:convert"; import "dart:developer"; - import 'package:dio/dio.dart'; + import 'package:anyway/constants.dart'; import "package:anyway/structs/landmark.dart"; import "package:anyway/structs/trip.dart"; @@ -25,14 +25,14 @@ Dio dio = Dio( ), ); -Future? fetchTrip( - List startPoint, +fetchTrip( + Trip trip, Future preferences, ) async { UserPreferences prefs = await preferences; Map data = { "preferences": prefs.toJson(), - "start": startPoint + "start": trip.landmarks!.first.location, }; String dataString = jsonEncode(data); log(dataString); @@ -44,24 +44,25 @@ Future? fetchTrip( // handle errors if (response.statusCode != 200) { + trip.updateUUID("error"); throw Exception('Failed to load trip'); } if (response.data["error"] != null) { + trip.updateUUID("error"); throw Exception(response.data["error"]); } log(response.data.toString()); Map json = response.data; - // only fetch the trip "meta" data for now - Trip trip = Trip.fromJson(json); + // only fill in the trip "meta" data for now + trip.loadFromJson(json); String? nextUUID = json["first_landmark_uuid"]; while (nextUUID != null) { var (landmark, newUUID) = await fetchLandmark(nextUUID); - trip.landmarks.add(landmark); + trip.addLandmark(landmark); nextUUID = newUUID; } - return trip; } diff --git a/frontend/lib/utils/load_trips.dart b/frontend/lib/utils/load_trips.dart index 22b9ced..f608294 100644 --- a/frontend/lib/utils/load_trips.dart +++ b/frontend/lib/utils/load_trips.dart @@ -17,7 +17,7 @@ Future> loadTrips() async { } if (trips.isEmpty) { - Trip t1 = Trip(uuid: '1', cityName: 'Paris', landmarks: LinkedList()); + Trip t1 = Trip(uuid: '1', landmarks: LinkedList()); t1.landmarks.add( Landmark( uuid: '1', @@ -66,7 +66,7 @@ Future> loadTrips() async { trips.add(t1); - Trip t2 = Trip(uuid: '2', cityName: 'Vienna', landmarks: LinkedList()); + Trip t2 = Trip(uuid: '2', landmarks: LinkedList()); t2.landmarks.add( Landmark( diff --git a/frontend/pubspec.lock b/frontend/pubspec.lock index 8d3dc88..a891618 100644 --- a/frontend/pubspec.lock +++ b/frontend/pubspec.lock @@ -25,6 +25,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "4a5d8d2c728b0f3d0245f69f921d7be90cae4c2fd5288f773088672c0893f819" + url: "https://pub.dev" + source: hosted + version: "3.4.0" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: ff0c949e323d2a1b52be73acce5b4a7b04063e61414c8ca542dbba47281630a7 + url: "https://pub.dev" + source: hosted + version: "4.1.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "6322dde7a5ad92202e64df659241104a43db20ed594c41ca18de1014598d7996" + url: "https://pub.dev" + source: hosted + version: "1.3.0" characters: dependency: transitive description: @@ -168,6 +192,38 @@ packages: description: flutter source: sdk version: "0.0.0" + geocoding: + dependency: "direct main" + description: + name: geocoding + sha256: d580c801cba9386b4fac5047c4c785a4e19554f46be42f4f5e5b7deacd088a66 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + geocoding_android: + dependency: transitive + description: + name: geocoding_android + sha256: "1b13eca79b11c497c434678fed109c2be020b158cec7512c848c102bc7232603" + url: "https://pub.dev" + source: hosted + version: "3.3.1" + geocoding_ios: + dependency: transitive + description: + name: geocoding_ios + sha256: "94ddba60387501bd1c11e18dca7c5a9e8c645d6e3da9c38b9762434941870c24" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + geocoding_platform_interface: + dependency: transitive + description: + name: geocoding_platform_interface + sha256: "8c2c8226e5c276594c2e18bfe88b19110ed770aeb7c1ab50ede570be8b92229b" + url: "https://pub.dev" + source: hosted + version: "3.2.0" google_maps: dependency: transitive description: @@ -296,6 +352,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.12.0" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" path: dependency: transitive description: diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index 6f6d8cf..a2f41d4 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -41,6 +41,8 @@ dependencies: dio: ^5.5.0+1 google_maps_flutter: ^2.7.0 the_widget_marker: ^1.0.0 + cached_network_image: ^3.4.0 + geocoding: ^3.0.0 dev_dependencies: flutter_test: