From 8bc7da0b3efc9d79225101039276b1ab04e96e4e Mon Sep 17 00:00:00 2001 From: Remy Moll Date: Fri, 31 May 2024 21:33:04 +0200 Subject: [PATCH] first ui elements using the new structs --- frontend/lib/layout.dart | 4 +- frontend/lib/modules/destination_card.dart | 40 ------- frontend/lib/modules/greeter.dart | 44 ++++++++ frontend/lib/modules/landmark_card.dart | 73 +++++++++++++ frontend/lib/modules/landmarks_overview.dart | 101 ++++++++++++++++++ frontend/lib/modules/map.dart | 29 ++++- frontend/lib/modules/navigation.dart | 63 ----------- frontend/lib/{modules => pages}/overview.dart | 41 ++----- frontend/lib/{modules => pages}/profile.dart | 0 frontend/lib/structs/destination.dart | 62 ----------- frontend/lib/structs/landmark.dart | 56 ++++++++++ frontend/lib/structs/route.dart | 6 +- frontend/lib/utils/get_landmarks.dart | 25 +++++ frontend/lib/utils/get_route.dart | 18 ---- 14 files changed, 336 insertions(+), 226 deletions(-) delete mode 100644 frontend/lib/modules/destination_card.dart create mode 100644 frontend/lib/modules/greeter.dart create mode 100644 frontend/lib/modules/landmark_card.dart create mode 100644 frontend/lib/modules/landmarks_overview.dart delete mode 100644 frontend/lib/modules/navigation.dart rename frontend/lib/{modules => pages}/overview.dart (64%) rename frontend/lib/{modules => pages}/profile.dart (100%) delete mode 100644 frontend/lib/structs/destination.dart create mode 100644 frontend/lib/structs/landmark.dart create mode 100644 frontend/lib/utils/get_landmarks.dart delete mode 100644 frontend/lib/utils/get_route.dart diff --git a/frontend/lib/layout.dart b/frontend/lib/layout.dart index 875ccbc..1c7a1be 100644 --- a/frontend/lib/layout.dart +++ b/frontend/lib/layout.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:fast_network_navigation/modules/overview.dart'; -import 'package:fast_network_navigation/modules/profile.dart'; +import 'package:fast_network_navigation/pages/overview.dart'; +import 'package:fast_network_navigation/pages/profile.dart'; // BasePage is the scaffold that holds all other pages diff --git a/frontend/lib/modules/destination_card.dart b/frontend/lib/modules/destination_card.dart deleted file mode 100644 index 5829c5e..0000000 --- a/frontend/lib/modules/destination_card.dart +++ /dev/null @@ -1,40 +0,0 @@ - - -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - - -class DestinationCard extends StatefulWidget { - final String title; - final String description; - final String image; - bool visited; - - @override - _DestinationCardState createState() => _DestinationCardState(); - - - DestinationCard(this.title, this.description, this.image, this.visited); - - Widget build() { - return Card( - child: ListTile( - leading: Icon(Icons.location_on), - title: Text(title), - subtitle: Text(description), - onTap: () { - // Navigator.pushNamed(context, '/destination'); - }, - ), - ); - } - -} - -class _DestinationCardState extends State { - @override - Widget build(BuildContext context) { - return Card(); - } - -} \ No newline at end of file diff --git a/frontend/lib/modules/greeter.dart b/frontend/lib/modules/greeter.dart new file mode 100644 index 0000000..99c1be2 --- /dev/null +++ b/frontend/lib/modules/greeter.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; + +Widget Greeter (ThemeData theme, {bool full = false}) { + String greeterText = ""; + try { + String cityName = getCityName(); + greeterText = "Welcome to $cityName!"; + } catch (e) { + greeterText = "Welcome ..."; + } + + Widget topGreeter = Text( + greeterText, + style: TextStyle(color: theme.primaryColor, fontSize: 24.0, fontWeight: FontWeight.bold), + maxLines: 1, + ); + + Widget bottomGreeter = Container(); + if (full) { + bottomGreeter = Text( + "Busy day ahead? Here is how to make the most of it!", + style: TextStyle(color: Colors.black, fontSize: 18.0), + maxLines: 1, + ); + } + Widget greeter = Center( + child: Column( + children: [ + if (!full) Padding(padding: EdgeInsets.only(top: 24.0)), + topGreeter, + if (full) bottomGreeter, + Padding(padding: EdgeInsets.only(bottom: 24.0)), + ], + ), + ); + + return greeter; +} + + + +String getCityName() { + return "Paris"; +} \ No newline at end of file diff --git a/frontend/lib/modules/landmark_card.dart b/frontend/lib/modules/landmark_card.dart new file mode 100644 index 0000000..78bf280 --- /dev/null +++ b/frontend/lib/modules/landmark_card.dart @@ -0,0 +1,73 @@ +import 'package:fast_network_navigation/structs/landmark.dart'; +import 'package:flutter/material.dart'; + + +class LandmarkCard extends StatefulWidget { + final Landmark landmark; + @override + _LandmarkCardState createState() => _LandmarkCardState(); + + LandmarkCard(this.landmark); + +} + +class _LandmarkCardState extends State { + @override + Widget build(BuildContext context) { + ThemeData theme = Theme.of(context); + return Card( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15.0), + ), + child: Row( + children: [ + Container( + width: 160, + height: 160, + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(15.0), + bottomLeft: Radius.circular(15.0), + ), + image: DecorationImage( + image: NetworkImage('https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg'), + fit: BoxFit.cover, + ), + ), + ), + Padding( + padding: EdgeInsets.all(10), + child: Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.landmark.name, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + SizedBox(height: 5), + Text( + "${widget.landmark.name} (${widget.landmark.type.name})", + style: TextStyle(fontSize: 14), + ), + ], + ), + ), + ), + // Align( + // alignment: Alignment.topRight, + // child: Icon(Icons.push_pin, color: theme.primaryColor), + // ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/frontend/lib/modules/landmarks_overview.dart b/frontend/lib/modules/landmarks_overview.dart new file mode 100644 index 0000000..120fe0e --- /dev/null +++ b/frontend/lib/modules/landmarks_overview.dart @@ -0,0 +1,101 @@ +import 'package:fast_network_navigation/modules/landmark_card.dart'; +import 'package:fast_network_navigation/structs/landmark.dart'; +import 'package:fast_network_navigation/utils/get_landmarks.dart'; +import 'package:flutter/material.dart'; + + + + +class loadLandmarksOverview extends StatefulWidget { + const loadLandmarksOverview({super.key}); + + @override + State createState() => _loadLandmarksOverviewState(); +} + +class _loadLandmarksOverviewState extends State { + final Future> _landmarks = fetchLandmarks(); + + @override + Widget build(BuildContext context) { + 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!)]; + } else if (snapshot.hasError) { + children = [ + const Icon( + Icons.error_outline, + color: Colors.red, + size: 60, + ), + Padding( + padding: const EdgeInsets.only(top: 16), + child: Text('Error: ${snapshot.error}'), + ), + ]; + } else { + children = [LandmarkCard(Landmark(name: "loading", location: [0,0], type: LandmarkType(name: "loading")))]; + } + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: children, + ), + ); + }, + ), + ); + } +} + +Widget landmarksWithSteps(List landmarks) { + List children = []; + for (Landmark landmark in landmarks) { + children.add(LandmarkCard(landmark)); + children.add(stepBetweenLandmarks()); + } + + return Column( + children: children.sublist(0, children.length - 1) + ); +} + + +Widget stepBetweenLandmarks() { + // 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 + // There is also a button to open the navigation instructions as a new intent + return Row( + children: [ + Container( + width: 50, + height: 50, + decoration: BoxDecoration( + border: Border( + left: BorderSide(width: 1.0, color: Colors.black), + ), + ), + child: Column( + children: [ + Icon(Icons.directions_walk), + Text("5 min", style: TextStyle(fontSize: 10)), + ], + ), + ), + Spacer(), + ElevatedButton( + onPressed: () { + // Open navigation instructions + }, + child: Text("Navigate"), + ), + ], + ); +} \ No newline at end of file diff --git a/frontend/lib/modules/map.dart b/frontend/lib/modules/map.dart index 9f7a22d..8f88489 100644 --- a/frontend/lib/modules/map.dart +++ b/frontend/lib/modules/map.dart @@ -1,5 +1,5 @@ +import 'package:fast_network_navigation/structs/landmark.dart'; import 'package:flutter/material.dart'; - import 'package:google_maps_flutter/google_maps_flutter.dart'; class MapWidget extends StatefulWidget { @@ -9,16 +9,37 @@ class MapWidget extends StatefulWidget { class _MapWidgetState extends State { late GoogleMapController mapController; - - final LatLng _center = const LatLng(45.521563, -122.677433); + // coordinates of Paris + final LatLng _center = const LatLng(48.8566, 2.3522); void _onMapCreated(GoogleMapController controller) { mapController = controller; + addLandmarks(); } void _onCameraIdle() { // print(mapController.getLatLng()); } + void addLandmarks() { + // // adds a marker for each landmark + // List landmarks = [ + // Landmark(name: "Eiffel Tower", location: [48.8584, 2.2945], type: LandmarkType(name: "Type 1")), + // Landmark(name: "Louvre Museum", location: [48.8606, 2.3376], type: LandmarkType(name: "Type 1")), + // Landmark(name: "Notre-Dame Cathedral", location: [48.8529, 2.3499], type: LandmarkType(name: "Type 1")), + // Landmark(name: "Arc de Triomphe", location: [48.8738, 2.2950], type: LandmarkType(name: "Type 1")), + // Landmark(name: "Palace of Versailles", location: [48.8014, 2.1301], type: LandmarkType(name: "Type 1")), + // ]; + + // for (Landmark landmark in landmarks) { + // mapController. + // mapController.addMarker(MarkerOptions( + // position: LatLng(landmark.location[0], landmark.location[1]), + // infoWindowText: InfoWindowText(landmark.name, landmark.type.name), + // )); + // } + } + + @override Widget build(BuildContext context) { return GoogleMap( @@ -28,6 +49,8 @@ class _MapWidgetState extends State { zoom: 11.0, ), onCameraIdle: _onCameraIdle, + // onLongPress: , + // markers: #, ); } } diff --git a/frontend/lib/modules/navigation.dart b/frontend/lib/modules/navigation.dart deleted file mode 100644 index 7bd62ac..0000000 --- a/frontend/lib/modules/navigation.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:fast_network_navigation/modules/destination_card.dart'; -import 'package:flutter/material.dart'; - - -List loadDestinations() { - List cities = [ - singleDestination( - "New York", - "The Big Apple", - "https://upload.wikimedia.org/wikipedia/commons/thumb/3/34/View_of_New_York_City.jpg/800px-View_of_New_York_City.jpg" - ), - singleDestination( - "Los Angeles", - "City of Angels", - "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8d/Los_Angeles_City_Hall_2013.jpg/800px-Los_Angeles_City_Hall_2013.jpg" - ), - singleDestination( - "Chicago", - "The Windy City", - "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6f/Chicago_skyline%2C_viewed_from_John_Hancock_Center.jpg/800px-Chicago_skyline%2C_viewed_from_John_Hancock_Center.jpg" - ), - singleDestination( - "San Francisco", - "The Golden City", - "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/San_Francisco_City_Hall_2013.jpg/800px-San_Francisco_City_Hall_2013.jpg" - ), - singleDestination( - "Miami", - "The Magic City", - "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Miami_collage.jpg/800px-Miami_collage.jpg" - ), - singleDestination( - "Las Vegas", - "Sin City", - "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6e/Las_Vegas_Strip.jpg/800px-Las_Vegas_Strip.jpg" - ), - singleDestination( - "Seattle", - "Emerald City", - "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Seattle_Kerry_Park_Skyline.jpg/800px-Seattle_Kerry_Park_Skyline.jpg" - ), - singleDestination( - "Boston", - "Beantown", - "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0f/Boston_skyline_from_Longfellow_Bridge_September_2017_panorama_2.jpg/800px-Boston" - ) - ]; - cities.shuffle(); - return cities; -} - -Widget singleDestination(String title, String description, String image) { - return Card( - child: ListTile( - leading: Icon(Icons.location_on), - title: Text(title), - subtitle: Text(description), - onTap: () { - // Navigator.pushNamed(context, '/destination'); - }, - ), - ); -} \ No newline at end of file diff --git a/frontend/lib/modules/overview.dart b/frontend/lib/pages/overview.dart similarity index 64% rename from frontend/lib/modules/overview.dart rename to frontend/lib/pages/overview.dart index bfcf18f..ab6ffb8 100644 --- a/frontend/lib/modules/overview.dart +++ b/frontend/lib/pages/overview.dart @@ -1,10 +1,8 @@ +import 'package:fast_network_navigation/modules/greeter.dart'; import 'package:flutter/material.dart'; import 'package:sliding_up_panel/sliding_up_panel.dart'; -import 'package:geocode/geocode.dart'; -import 'dart:async'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:fast_network_navigation/modules/navigation.dart'; +import 'package:fast_network_navigation/modules/landmarks_overview.dart'; import 'package:fast_network_navigation/modules/map.dart'; @@ -16,25 +14,6 @@ class NavigationOverview extends StatefulWidget { -class Debounce { - Duration delay; - Timer? _timer; - - Debounce( - this.delay, - ); - - call(void Function() callback) { - _timer?.cancel(); - _timer = Timer(delay, callback); - } - - dispose() { - _timer?.cancel(); - } -} - - class _NavigationOverviewState extends State { @override @@ -53,9 +32,10 @@ class _NavigationOverviewState extends State { decoration: BoxDecoration( color: theme.canvasColor, borderRadius: BorderRadius.only(topLeft: Radius.circular(24.0), topRight: Radius.circular(24.0)), + boxShadow: [] ), - child: Greeting(theme) + child: Greeter(theme) ); } @@ -77,9 +57,8 @@ class _NavigationOverviewState extends State { child: SingleChildScrollView( child: Column( children: [ - Greeting(theme), - Text("Got a lot to do today! Here is a rundown:"), - ...loadDestinations(), + Greeter(theme, full: true), + loadLandmarksOverview(), ], ), ), @@ -88,12 +67,4 @@ class _NavigationOverviewState extends State { ); } - Widget Greeting (ThemeData theme) { - return Center( - child: Text( - "Explore #todo", - style: TextStyle(color: theme.primaryColor, fontSize: 24.0, fontWeight: FontWeight.bold), - ), - ); - } } diff --git a/frontend/lib/modules/profile.dart b/frontend/lib/pages/profile.dart similarity index 100% rename from frontend/lib/modules/profile.dart rename to frontend/lib/pages/profile.dart diff --git a/frontend/lib/structs/destination.dart b/frontend/lib/structs/destination.dart deleted file mode 100644 index c44fbf9..0000000 --- a/frontend/lib/structs/destination.dart +++ /dev/null @@ -1,62 +0,0 @@ -import "package:flutter/material.dart"; - -class Destination { - final double latitude; - final double longitude; - final String name; - final String description; - // final DestinationType type; - final Duration duration; - final bool visited; - - const Destination({ - required this.latitude, - required this.longitude, - required this.name, - required this.description, - // required this.type, - required this.duration, - required this.visited, - }); - - factory Destination.fromJson(Map json) { - return switch (json) { - { - 'lat': double latitude, - 'lon': double longitude, - 'name': String name, - 'description': String description, - // 'type': String type, - 'duration': int duration, - 'visited': bool visited - - } => - Destination( - latitude: latitude, - longitude: longitude, - name: name, - description: description, - // type: "DestinationType.values.firstWhere((element) => element.name == type)", - duration: Duration(minutes: duration), - visited: visited - ), - _ => throw const FormatException('Failed to load destination.'), - }; -} - -} - - -class DestinationType { - final String name; - final String description; - final Icon icon; - - const DestinationType({ - required this.name, - required this.description, - required this.icon, - }); -} - - diff --git a/frontend/lib/structs/landmark.dart b/frontend/lib/structs/landmark.dart new file mode 100644 index 0000000..a65dbe8 --- /dev/null +++ b/frontend/lib/structs/landmark.dart @@ -0,0 +1,56 @@ +class Landmark { + final String name; + final List location; + final LandmarkType type; + // final String description; + // final Duration duration; + // final bool visited; + + const Landmark({ + required this.name, + required this.location, + required this.type, + // required this.description, + // required this.duration, + // required this.visited, + }); + + factory Landmark.fromJson(Map json) { + return switch (json) { + { + 'loc': List location, + 'name': String name, + 'type': String type, + // 'description': String description, + // 'duration': int duration, + // 'visited': bool visited + + } => + Landmark( + name: name, + location: location, + type: LandmarkType(name: type) + // description: description, + // duration: Duration(minutes: duration), + // visited: visited + ), + _ => throw const FormatException('Failed to load destination.'), + }; + } + +} + + +class LandmarkType { + final String name; + // final String description; + // final Icon icon; + + const LandmarkType({ + required this.name, + // required this.description, + // required this.icon, + }); +} + + diff --git a/frontend/lib/structs/route.dart b/frontend/lib/structs/route.dart index 8b46787..62e0baa 100644 --- a/frontend/lib/structs/route.dart +++ b/frontend/lib/structs/route.dart @@ -1,14 +1,14 @@ -import "package:fast_network_navigation/structs/destination.dart"; +import "package:fast_network_navigation/structs/landmark.dart"; class Route { final String name; final Duration duration; - final List destinations; + final List landmarks; Route({ required this.name, required this.duration, - required this.destinations + required this.landmarks }); } \ No newline at end of file diff --git a/frontend/lib/utils/get_landmarks.dart b/frontend/lib/utils/get_landmarks.dart new file mode 100644 index 0000000..c538b0f --- /dev/null +++ b/frontend/lib/utils/get_landmarks.dart @@ -0,0 +1,25 @@ +import "package:fast_network_navigation/structs/landmark.dart"; +import 'package:http/http.dart' as http; + + +Future> fetchLandmarks() async { + // final response = await http + // .get(Uri.parse('https://nav.kluster.moll.re/v1/destination/1')); + + // if (response.statusCode == 200) { + // If the server did return a 200 OK response, + // then parse the JSON. + List landmarks = [ + Landmark(name: "Landmark 1", location: [0, 0], type: LandmarkType(name: "Type 1")), + Landmark(name: "Landmark 2", location: [0, 0], type: LandmarkType(name: "Type 2")), + Landmark(name: "Landmark 3", location: [0, 0], type: LandmarkType(name: "Type 3")), + Landmark(name: "Landmark 4", location: [0, 0], type: LandmarkType(name: "Type 4")), + Landmark(name: "Landmark 5", location: [0, 0], type: LandmarkType(name: "Type 5")), + ]; + return landmarks; + // } else { + // // If the server did not return a 200 OK response, + // // then throw an exception. + // throw Exception('Failed to load destination'); + // } +} \ No newline at end of file diff --git a/frontend/lib/utils/get_route.dart b/frontend/lib/utils/get_route.dart deleted file mode 100644 index fccf6ac..0000000 --- a/frontend/lib/utils/get_route.dart +++ /dev/null @@ -1,18 +0,0 @@ -import "package:fast_network_navigation/structs/destination.dart"; -import 'package:http/http.dart' as http; -import 'dart:convert'; - -Future fetchDestination() async { - final response = await http - .get(Uri.parse('https://nav.kluster.moll.re/v1/destination/1')); - - if (response.statusCode == 200) { - // If the server did return a 200 OK response, - // then parse the JSON. - return Destination.fromJson(jsonDecode(response.body) as Map); - } else { - // If the server did not return a 200 OK response, - // then throw an exception. - throw Exception('Failed to load destination'); - } -} \ No newline at end of file