From 3029fb85374ded3b3f713b9f6d4ad4c5c96ecfa7 Mon Sep 17 00:00:00 2001 From: Remy Moll Date: Sat, 25 May 2024 18:55:58 +0200 Subject: [PATCH] some preference improvements --- .../android/app/src/main/AndroidManifest.xml | 3 + .../{modules/scaffold.dart => layout.dart} | 8 +- frontend/lib/main.dart | 2 +- frontend/lib/modules/overview.dart | 6 +- frontend/lib/modules/profile.dart | 114 ++++++++++------ frontend/lib/structs/destination.dart | 39 +++++- frontend/lib/structs/preferences.dart | 82 ++++++++++++ frontend/lib/structs/route.dart | 14 ++ frontend/lib/utils/get_route.dart | 18 +++ .../Flutter/GeneratedPluginRegistrant.swift | 2 + frontend/pubspec.lock | 124 +++++++++++++++++- frontend/pubspec.yaml | 2 + frontend/test/widget_test.dart | 7 +- 13 files changed, 365 insertions(+), 56 deletions(-) rename frontend/lib/{modules/scaffold.dart => layout.dart} (92%) create mode 100644 frontend/lib/structs/preferences.dart create mode 100644 frontend/lib/structs/route.dart create mode 100644 frontend/lib/utils/get_route.dart diff --git a/frontend/android/app/src/main/AndroidManifest.xml b/frontend/android/app/src/main/AndroidManifest.xml index 58e6f22..f144d58 100644 --- a/frontend/android/app/src/main/AndroidManifest.xml +++ b/frontend/android/app/src/main/AndroidManifest.xml @@ -48,4 +48,7 @@ + + + diff --git a/frontend/lib/modules/scaffold.dart b/frontend/lib/layout.dart similarity index 92% rename from frontend/lib/modules/scaffold.dart rename to frontend/lib/layout.dart index 822f208..875ccbc 100644 --- a/frontend/lib/modules/scaffold.dart +++ b/frontend/lib/layout.dart @@ -4,9 +4,10 @@ import 'package:fast_network_navigation/modules/overview.dart'; import 'package:fast_network_navigation/modules/profile.dart'; +// BasePage is the scaffold that holds all other pages +// A side drawer is used to switch between pages class BasePage extends StatefulWidget { const BasePage({super.key, required this.title}); - final String title; @override @@ -22,7 +23,7 @@ class _BasePageState extends State { }); } - Widget currentView = MainPage(); + Widget currentView = NavigationOverview(); @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); @@ -51,7 +52,7 @@ class _BasePageState extends State { // Update the state of the app _onItemTapped(0); // Then close the drawer - currentView = MainPage(); + currentView = NavigationOverview(); Navigator.pop(context); }, ), @@ -87,3 +88,4 @@ class _BasePageState extends State { ); } } + diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index 424c0d5..d818628 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:fast_network_navigation/modules/scaffold.dart'; +import 'package:fast_network_navigation/layout.dart'; void main() => runApp(const App()); diff --git a/frontend/lib/modules/overview.dart b/frontend/lib/modules/overview.dart index d9bc126..bfcf18f 100644 --- a/frontend/lib/modules/overview.dart +++ b/frontend/lib/modules/overview.dart @@ -9,9 +9,9 @@ import 'package:fast_network_navigation/modules/map.dart'; -class MainPage extends StatefulWidget { +class NavigationOverview extends StatefulWidget { @override - State createState() => _MainPageState(); + State createState() => _NavigationOverviewState(); } @@ -35,7 +35,7 @@ class Debounce { } -class _MainPageState extends State { +class _NavigationOverviewState extends State { @override Widget build(BuildContext context) { diff --git a/frontend/lib/modules/profile.dart b/frontend/lib/modules/profile.dart index 69bfba4..e8beb08 100644 --- a/frontend/lib/modules/profile.dart +++ b/frontend/lib/modules/profile.dart @@ -1,3 +1,4 @@ +import 'package:fast_network_navigation/structs/preferences.dart'; import 'package:flutter/material.dart'; @@ -8,48 +9,83 @@ class ProfilePage extends StatefulWidget { } class _ProfilePageState extends State { - - double value = 0.0; - void onChanged(double newValue) { - setState(() { - value = newValue; - }); - } @override Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('Profile'), - ), - body: Padding( - padding: EdgeInsets.all(8.0), - child: Column( - children: [ - Card( - child: ListTile( - leading: Icon(Icons.notifications_sharp), - title: Text('Notification 1'), - subtitle: Text('This is a first notification'), - ), - ), - Card( - child: ListTile( - leading: Icon(Icons.notifications_sharp), - title: Text('Notification 2'), - subtitle: Text('This is a notification'), - ), - ), - Card( - child: ListTile( - leading: Icon(Icons.outdoor_grill), - title: Text("Eating preference"), - subtitle: Slider.adaptive(value: value, onChanged: onChanged, min: 0, max: 5, divisions: 5, label: value.toInt().toString(),) - - ) - ) - ], + return ListView( + children: [ + // First a round, centered image + Center( + child: CircleAvatar( + radius: 100, + child: Icon(Icons.person, size: 100), + ) ), - ) + Center( + child: Text('Curious traveler', style: TextStyle(fontSize: 24)) + ), + + Padding( + padding: EdgeInsets.all(10), + ), + + Text('Please rate your preferences for the following activities:'), + + // Now the sliders + ImportanceSliders() + ] ); } } + + + +class ImportanceSliders extends StatefulWidget { + + @override + State createState() => _ImportanceSlidersState(); +} + + +class _ImportanceSlidersState extends State { + + UserPreferences _prefs = UserPreferences(); + + @override + void initState() { + super.initState(); + _prefs.load(); + } + + List _createSliders() { + List sliders = []; + for (SinglePreference pref in _prefs.preferences) { + sliders.add(Card( + child: ListTile( + leading: pref.icon, + title: Text(pref.name), + subtitle: Slider( + value: pref.value.toDouble(), + min: 0, + max: 10, + divisions: 10, + label: pref.value.toString(), + onChanged: (double newValue) { + setState(() { + pref.value = newValue.toInt(); + _prefs.save(); + }); + }, + ) + ), + margin: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: 0), + shadowColor: Colors.grey, + )); + } + return sliders; + } + + @override + Widget build(BuildContext context) { + return Column(children: _createSliders()); + } +} diff --git a/frontend/lib/structs/destination.dart b/frontend/lib/structs/destination.dart index bb8d086..c44fbf9 100644 --- a/frontend/lib/structs/destination.dart +++ b/frontend/lib/structs/destination.dart @@ -1,31 +1,62 @@ +import "package:flutter/material.dart"; class Destination { final double latitude; final double longitude; final String name; final String description; - final DestinationType type; + // final DestinationType type; final Duration duration; final bool visited; - Destination({ + const Destination({ required this.latitude, required this.longitude, required this.name, required this.description, - required this.type, + // 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; - DestinationType({ + const DestinationType({ required this.name, required this.description, + required this.icon, }); } + + diff --git a/frontend/lib/structs/preferences.dart b/frontend/lib/structs/preferences.dart new file mode 100644 index 0000000..7746393 --- /dev/null +++ b/frontend/lib/structs/preferences.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + + +class SinglePreference { + String name; + String description; + int value; + Icon icon; + String key; + + SinglePreference({ + required this.name, + required this.description, + required this.value, + required this.icon, + required this.key, + }); +} + + +class UserPreferences { + List preferences = [ + SinglePreference( + name: "Sightseeing", + description: "How much do you like sightseeing?", + value: 0, + icon: Icon(Icons.church), + key: "sightseeing", + ), + SinglePreference( + name: "Shopping", + description: "How much do you like shopping?", + value: 0, + icon: Icon(Icons.shopping_bag), + key: "shopping", + ), + SinglePreference( + name: "Foods & Drinks", + description: "How much do you like eating?", + value: 0, + icon: Icon(Icons.restaurant), + key: "eating", + ), + SinglePreference( + name: "Nightlife", + description: "How much do you like nightlife?", + value: 0, + icon: Icon(Icons.wine_bar), + key: "nightlife", + ), + SinglePreference( + name: "Nature", + description: "How much do you like nature?", + value: 0, + icon: Icon(Icons.landscape), + key: "nature", + ), + SinglePreference( + name: "Culture", + description: "How much do you like culture?", + value: 0, + icon: Icon(Icons.palette), + key: "culture", + ), + ]; + + + void save() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + for (SinglePreference pref in preferences) { + prefs.setInt(pref.key, pref.value); + } + } + + void load() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + for (SinglePreference pref in preferences) { + pref.value = prefs.getInt(pref.key) ?? 0; + } + } +} \ No newline at end of file diff --git a/frontend/lib/structs/route.dart b/frontend/lib/structs/route.dart new file mode 100644 index 0000000..8b46787 --- /dev/null +++ b/frontend/lib/structs/route.dart @@ -0,0 +1,14 @@ +import "package:fast_network_navigation/structs/destination.dart"; + + +class Route { + final String name; + final Duration duration; + final List destinations; + + Route({ + required this.name, + required this.duration, + required this.destinations + }); +} \ No newline at end of file diff --git a/frontend/lib/utils/get_route.dart b/frontend/lib/utils/get_route.dart new file mode 100644 index 0000000..fccf6ac --- /dev/null +++ b/frontend/lib/utils/get_route.dart @@ -0,0 +1,18 @@ +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 diff --git a/frontend/macos/Flutter/GeneratedPluginRegistrant.swift b/frontend/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..724bb2a 100644 --- a/frontend/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/frontend/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/frontend/pubspec.lock b/frontend/pubspec.lock index 21fb46f..7c2f396 100644 --- a/frontend/pubspec.lock +++ b/frontend/pubspec.lock @@ -65,6 +65,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" flutter: dependency: "direct main" description: flutter @@ -193,7 +209,7 @@ packages: source: hosted version: "0.15.4" http: - dependency: transitive + dependency: "direct main" description: name: http sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" @@ -288,6 +304,38 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + platform: + dependency: transitive + description: + name: platform + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://pub.dev" + source: hosted + version: "3.1.4" plugin_platform_interface: dependency: transitive description: @@ -304,6 +352,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" sky_engine: dependency: transitive description: flutter @@ -405,6 +509,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.1" + win32: + dependency: transitive + description: + name: win32 + sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + url: "https://pub.dev" + source: hosted + version: "5.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" sdks: - dart: ">=3.3.4 <4.0.0" + dart: ">=3.4.0 <4.0.0" flutter: ">=3.19.0" diff --git a/frontend/pubspec.yaml b/frontend/pubspec.yaml index b0409b2..9f1732c 100644 --- a/frontend/pubspec.yaml +++ b/frontend/pubspec.yaml @@ -39,6 +39,8 @@ dependencies: geocoding: ^3.0.0 geocode: ^1.0.3 google_maps_flutter: ^2.6.1 + http: ^1.2.1 + shared_preferences: ^2.2.3 dev_dependencies: flutter_test: diff --git a/frontend/test/widget_test.dart b/frontend/test/widget_test.dart index 1eacbc4..143b24f 100644 --- a/frontend/test/widget_test.dart +++ b/frontend/test/widget_test.dart @@ -9,16 +9,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; // import 'package:fast_network_navigation/main.dart'; -import 'package:fast_network_navigation/modules/scaffold.dart'; +import 'package:fast_network_navigation/layout.dart'; void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. await tester.pumpWidget(BasePage(title: "City Nav")); - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); + // Verfiy that the title is displayed + expect(find.text('City Nav'), findsOneWidget); // Tap the '+' icon and trigger a frame. await tester.tap(find.byIcon(Icons.add));