From d5e0b7d51a9fa8d2b3e4df110188437f44f8bd3f Mon Sep 17 00:00:00 2001 From: Remy Moll Date: Mon, 3 Jun 2024 13:51:01 +0200 Subject: [PATCH] Beginning to use different contexts --- .../android/app/src/main/AndroidManifest.xml | 2 +- frontend/lib/layout.dart | 102 ++++++++++++------ frontend/lib/main.dart | 2 +- frontend/lib/modules/greeter.dart | 4 +- frontend/lib/modules/landmarks_overview.dart | 2 +- frontend/lib/modules/trips_overview.dart | 65 +++++++++++ frontend/lib/pages/new_trip.dart | 30 ++++++ frontend/lib/pages/profile.dart | 2 +- frontend/lib/pages/tutorial.dart | 27 +++++ frontend/lib/structs/landmark.dart | 11 ++ frontend/lib/structs/trip.dart | 25 +++++ frontend/lib/utils/get_landmarks.dart | 2 + frontend/lib/utils/get_trips.dart | 37 +++++++ frontend/test/widget_test.dart | 2 +- 14 files changed, 271 insertions(+), 42 deletions(-) create mode 100644 frontend/lib/modules/trips_overview.dart create mode 100644 frontend/lib/pages/new_trip.dart create mode 100644 frontend/lib/pages/tutorial.dart create mode 100644 frontend/lib/structs/trip.dart create mode 100644 frontend/lib/utils/get_trips.dart diff --git a/frontend/android/app/src/main/AndroidManifest.xml b/frontend/android/app/src/main/AndroidManifest.xml index f144d58..b38e92b 100644 --- a/frontend/android/app/src/main/AndroidManifest.xml +++ b/frontend/android/app/src/main/AndroidManifest.xml @@ -50,5 +50,5 @@ - + diff --git a/frontend/lib/layout.dart b/frontend/lib/layout.dart index 4d2d85f..6612e48 100644 --- a/frontend/lib/layout.dart +++ b/frontend/lib/layout.dart @@ -1,85 +1,117 @@ +import 'package:fast_network_navigation/modules/trips_overview.dart'; +import 'package:fast_network_navigation/pages/new_trip.dart'; +import 'package:fast_network_navigation/pages/tutorial.dart'; +import 'package:fast_network_navigation/structs/trip.dart'; import 'package:flutter/material.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 // A side drawer is used to switch between pages class BasePage extends StatefulWidget { - const BasePage({super.key, required this.title}); - final String title; + final String mainScreen; + final String currentMap; + final List trips; + + const BasePage({super.key, required this.mainScreen, this.currentMap = "map", this.trips = const []}); @override State createState() => _BasePageState(); } class _BasePageState extends State { - int _selectedIndex = 0; - - void _onItemTapped(int index) { - setState(() { - _selectedIndex = index; - }); - } - Widget currentView = NavigationOverview(); + @override Widget build(BuildContext context) { + Widget currentView = const Text("loading..."); + if (widget.mainScreen == "map") { + currentView = NavigationOverview(); + } else if (widget.mainScreen == "tutorial") { + currentView = TutorialPage(); + } else if (widget.mainScreen == "profile") { + currentView = ProfilePage(); + } + final ThemeData theme = Theme.of(context); return Scaffold( - appBar: AppBar(title: Text(widget.title)), + appBar: AppBar(title: Text("City Nav")), body: Center(child: currentView), drawer: Drawer( // Add a ListView to the drawer. This ensures the user can scroll // through the options in the drawer if there isn't enough vertical // space to fit everything. - child: ListView( - // Important: Remove any padding from the ListView. - padding: EdgeInsets.zero, + child: Column( children: [ DrawerHeader( decoration: BoxDecoration( gradient: LinearGradient(colors: [Colors.cyan, theme.primaryColor]) ), - child: const Text('The fanciest navigation!'), + child: Center( + child: Text( + 'City Nav', + style: TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.bold, + ), + ), + ), ), ListTile( title: const Text('Start'), leading: const Icon(Icons.map), - selected: _selectedIndex == 0, + selected: widget.mainScreen == "map", onTap: () { - // Update the state of the app - _onItemTapped(0); - // Then close the drawer - currentView = NavigationOverview(); - Navigator.pop(context); + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => BasePage(mainScreen: "map") + ) + ); }, ), + ListTile( + title: const Text('Trip Overview'), + leading: const Icon(Icons.list), + trailing: ElevatedButton( + onPressed: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const NewTripPage() + ) + ); + }, + child: const Text('New'), + ), + ), + Expanded(child: TripsOverview()), + const Divider(), ListTile( title: const Text('How to use'), leading: Icon(Icons.help), - selected: _selectedIndex == 1, + selected: widget.mainScreen == "tutorial", onTap: () { - // Update the state of the app - _onItemTapped(1); - currentView = const Text("ghfhggfhgf"); - - // Then close the drawer - Navigator.pop(context); + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => BasePage(mainScreen: "tutorial") + ) + ); }, ), - const Divider(), + ListTile( title: const Text('Settings'), leading: const Icon(Icons.settings), - selected: _selectedIndex == 2, + selected: widget.mainScreen == "profile", onTap: () { - _onItemTapped(2); - currentView = ProfilePage(); - Navigator.pop(context); - }, + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => BasePage(mainScreen: "profile") + ) + ); + }, ), // settings in the bottom of the drawer ], diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index d818628..1fd9fc2 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -12,7 +12,7 @@ class App extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: appTitle, - home: BasePage(title: appTitle), + home: BasePage(mainScreen: "map"), theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.green), ); } diff --git a/frontend/lib/modules/greeter.dart b/frontend/lib/modules/greeter.dart index 99c1be2..e3066ac 100644 --- a/frontend/lib/modules/greeter.dart +++ b/frontend/lib/modules/greeter.dart @@ -19,8 +19,8 @@ Widget Greeter (ThemeData theme, {bool full = false}) { 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, + style: TextStyle(color: Colors.black, fontSize: 18), + textAlign: TextAlign.center, ); } Widget greeter = Center( diff --git a/frontend/lib/modules/landmarks_overview.dart b/frontend/lib/modules/landmarks_overview.dart index 120fe0e..c967a65 100644 --- a/frontend/lib/modules/landmarks_overview.dart +++ b/frontend/lib/modules/landmarks_overview.dart @@ -40,7 +40,7 @@ class _loadLandmarksOverviewState extends State { ), ]; } else { - children = [LandmarkCard(Landmark(name: "loading", location: [0,0], type: LandmarkType(name: "loading")))]; + children = [Center(child: CircularProgressIndicator())]; } return Center( child: Column( diff --git a/frontend/lib/modules/trips_overview.dart b/frontend/lib/modules/trips_overview.dart new file mode 100644 index 0000000..955a109 --- /dev/null +++ b/frontend/lib/modules/trips_overview.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; + +import 'package:fast_network_navigation/layout.dart'; +import 'package:fast_network_navigation/structs/trip.dart'; +import 'package:fast_network_navigation/utils/get_trips.dart'; + + +class TripsOverview extends StatefulWidget { + + const TripsOverview({super.key}); + + @override + State createState() => _TripsOverviewState(); +} + +class _TripsOverviewState extends State { + final Future> _trips = loadTrips(); + + + Widget listBuild (BuildContext context, AsyncSnapshot> snapshot) { + List children; + if (snapshot.hasData) { + children = List.generate(snapshot.data!.length, (index) { + Trip trip = snapshot.data![index]; + return ListTile( + title: Text("Trip to ${trip.cityName} (${trip.landmarks.length} stops)"), + leading: Icon(Icons.pin_drop), + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => BasePage(mainScreen: "map") //, trip: trip) + ) + ); + }, + ); + }); + } 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 = [Center(child: CircularProgressIndicator())]; + } + + return ListView( + children: children, + ); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: _trips, + builder: listBuild, + ); + } +} \ No newline at end of file diff --git a/frontend/lib/pages/new_trip.dart b/frontend/lib/pages/new_trip.dart new file mode 100644 index 0000000..47e2c5b --- /dev/null +++ b/frontend/lib/pages/new_trip.dart @@ -0,0 +1,30 @@ + +import 'package:flutter/material.dart'; + +class NewTripPage extends StatefulWidget { + const NewTripPage({Key? key}) : super(key: key); + + @override + _NewTripPageState createState() => _NewTripPageState(); +} + +class _NewTripPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('New Trip'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + 'Create a new trip', + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/frontend/lib/pages/profile.dart b/frontend/lib/pages/profile.dart index 42f08d7..10fefe8 100644 --- a/frontend/lib/pages/profile.dart +++ b/frontend/lib/pages/profile.dart @@ -30,7 +30,7 @@ class _ProfilePageState extends State { Padding( padding: EdgeInsets.only(left: 10, right: 10, top: 0, bottom: 10), - child: Text('Please rate your personal preferences so that we can taylor your experience.', style: TextStyle(fontSize: 20), ) + child: Text('Please rate your personal preferences so that we can taylor your experience.', style: TextStyle(fontSize: 18)) ), // Now the sliders diff --git a/frontend/lib/pages/tutorial.dart b/frontend/lib/pages/tutorial.dart new file mode 100644 index 0000000..3b19421 --- /dev/null +++ b/frontend/lib/pages/tutorial.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; + + + +class TutorialPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Tutorial"), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'Welcome to the tutorial page!', + ), + Text( + 'This is where you will learn how to use the app.', + ), + ], + ), + ), + ); + } +} diff --git a/frontend/lib/structs/landmark.dart b/frontend/lib/structs/landmark.dart index a65dbe8..14d1b5b 100644 --- a/frontend/lib/structs/landmark.dart +++ b/frontend/lib/structs/landmark.dart @@ -38,6 +38,17 @@ class Landmark { }; } + + Map toJson() { + return { + 'name': name, + 'location': location, + 'type': type.name, + // 'description': description, + // 'duration': duration.inMinutes, + // 'visited': visited + }; + } } diff --git a/frontend/lib/structs/trip.dart b/frontend/lib/structs/trip.dart new file mode 100644 index 0000000..dc02332 --- /dev/null +++ b/frontend/lib/structs/trip.dart @@ -0,0 +1,25 @@ +// Represents a collection of landmarks that represent a journey +// Different instances of a Trip can be saved and loaded by the user + +import 'package:fast_network_navigation/structs/landmark.dart'; + +class Trip { + final String uuid; + final String cityName; + final List landmarks; + + + Trip({required this.uuid, required this.cityName, required this.landmarks}); + + factory Trip.fromJson(Map json) { + List landmarks = []; + for (var landmark in json['landmarks']) { + landmarks.add(Landmark.fromJson(landmark)); + } + return Trip( + uuid: json['uuid'], + cityName: json['cityName'], + landmarks: landmarks, + ); + } +} \ No newline at end of file diff --git a/frontend/lib/utils/get_landmarks.dart b/frontend/lib/utils/get_landmarks.dart index 3c47728..5390849 100644 --- a/frontend/lib/utils/get_landmarks.dart +++ b/frontend/lib/utils/get_landmarks.dart @@ -16,6 +16,8 @@ Future> fetchLandmarks() async { Landmark(name: "Landmark 4", location: [48.9, 2.4], type: LandmarkType(name: "Type 4")), Landmark(name: "Landmark 5", location: [48.91, 2.45], type: LandmarkType(name: "Type 5")), ]; + // sleep 10 seconds + await Future.delayed(Duration(seconds: 10)); return landmarks; // } else { // // If the server did not return a 200 OK response, diff --git a/frontend/lib/utils/get_trips.dart b/frontend/lib/utils/get_trips.dart new file mode 100644 index 0000000..0a64080 --- /dev/null +++ b/frontend/lib/utils/get_trips.dart @@ -0,0 +1,37 @@ +import 'dart:convert'; + +import 'package:fast_network_navigation/structs/trip.dart'; +import 'package:fast_network_navigation/structs/landmark.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +Future> loadTrips() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + + Set keys = prefs.getKeys(); + List itineraries = []; + for (String key in keys) { + if (key.startsWith("itinerary_")) { + String json = prefs.getString(key)!; + itineraries.add(Trip.fromJson(jsonDecode(json))); + } + } + itineraries.add(Trip(uuid: "1", cityName: "Paris", landmarks: [ + Landmark(name: "Landmark 1", location: [48.85, 2.35], type: LandmarkType(name: "Type 1")), + Landmark(name: "Landmark 2", location: [48.86, 2.36], type: LandmarkType(name: "Type 2")), + Landmark(name: "Landmark 3", location: [48.75, 2.3], type: LandmarkType(name: "Type 3")), + Landmark(name: "Landmark 4", location: [48.9, 2.4], type: LandmarkType(name: "Type 4")), + Landmark(name: "Landmark 5", location: [48.91, 2.45], type: LandmarkType(name: "Type 5")), + ])); + itineraries.add(Trip(uuid: "2", cityName: "Vienna", landmarks: [])); + itineraries.add(Trip(uuid: "3", cityName: "London", landmarks: [])); + itineraries.add(Trip(uuid: "4", cityName: "Madrid", landmarks: [])); + itineraries.add(Trip(uuid: "5", cityName: "Tokyo", landmarks: [])); + itineraries.add(Trip(uuid: "6", cityName: "New York", landmarks: [])); + itineraries.add(Trip(uuid: "7", cityName: "Los Angeles", landmarks: [])); + itineraries.add(Trip(uuid: "8", cityName: "Zurich", landmarks: [])); + itineraries.add(Trip(uuid: "9", cityName: "Orschwiller", landmarks: [])); + + await Future.delayed(Duration(seconds: 3)); + + return itineraries; +} diff --git a/frontend/test/widget_test.dart b/frontend/test/widget_test.dart index 143b24f..f17a55a 100644 --- a/frontend/test/widget_test.dart +++ b/frontend/test/widget_test.dart @@ -14,7 +14,7 @@ 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")); + await tester.pumpWidget(BasePage(mainScreen: "map",)); // Verfiy that the title is displayed expect(find.text('City Nav'), findsOneWidget);