functional datastructure. Needs to be able to write to storage as well
This commit is contained in:
parent
9a5ae95d97
commit
db41528702
@ -2,6 +2,7 @@ import 'package:fast_network_navigation/modules/trips_overview.dart';
|
|||||||
import 'package:fast_network_navigation/pages/new_trip.dart';
|
import 'package:fast_network_navigation/pages/new_trip.dart';
|
||||||
import 'package:fast_network_navigation/pages/tutorial.dart';
|
import 'package:fast_network_navigation/pages/tutorial.dart';
|
||||||
import 'package:fast_network_navigation/structs/trip.dart';
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
|
import 'package:fast_network_navigation/utils/load_trips.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:fast_network_navigation/pages/overview.dart';
|
import 'package:fast_network_navigation/pages/overview.dart';
|
||||||
@ -11,10 +12,13 @@ import 'package:fast_network_navigation/pages/profile.dart';
|
|||||||
// A side drawer is used to switch between pages
|
// A side drawer is used to switch between pages
|
||||||
class BasePage extends StatefulWidget {
|
class BasePage extends StatefulWidget {
|
||||||
final String mainScreen;
|
final String mainScreen;
|
||||||
final String currentMap;
|
final Trip? trip;
|
||||||
final List<Trip> trips;
|
|
||||||
|
|
||||||
const BasePage({super.key, required this.mainScreen, this.currentMap = "map", this.trips = const []});
|
const BasePage({
|
||||||
|
super.key,
|
||||||
|
required this.mainScreen,
|
||||||
|
this.trip
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<BasePage> createState() => _BasePageState();
|
State<BasePage> createState() => _BasePageState();
|
||||||
@ -22,12 +26,15 @@ class BasePage extends StatefulWidget {
|
|||||||
|
|
||||||
class _BasePageState extends State<BasePage> {
|
class _BasePageState extends State<BasePage> {
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget currentView = const Text("loading...");
|
Widget currentView = const Text("loading...");
|
||||||
|
Future<List<Trip>> trips = loadTrips();
|
||||||
|
Future<Trip> firstTrip = getFirstTrip(trips);
|
||||||
|
// Future<Trip> trip = Future(trips[0]);
|
||||||
|
|
||||||
if (widget.mainScreen == "map") {
|
if (widget.mainScreen == "map") {
|
||||||
currentView = NavigationOverview();
|
currentView = NavigationOverview(trip: firstTrip);
|
||||||
} else if (widget.mainScreen == "tutorial") {
|
} else if (widget.mainScreen == "tutorial") {
|
||||||
currentView = TutorialPage();
|
currentView = TutorialPage();
|
||||||
} else if (widget.mainScreen == "profile") {
|
} else if (widget.mainScreen == "profile") {
|
||||||
@ -40,9 +47,6 @@ class _BasePageState extends State<BasePage> {
|
|||||||
appBar: AppBar(title: Text("City Nav")),
|
appBar: AppBar(title: Text("City Nav")),
|
||||||
body: Center(child: currentView),
|
body: Center(child: currentView),
|
||||||
drawer: Drawer(
|
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: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
DrawerHeader(
|
DrawerHeader(
|
||||||
@ -61,20 +65,10 @@ class _BasePageState extends State<BasePage> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Start'),
|
title: const Text('Your Trips'),
|
||||||
leading: const Icon(Icons.map),
|
leading: const Icon(Icons.map),
|
||||||
selected: widget.mainScreen == "map",
|
selected: widget.mainScreen == "map",
|
||||||
onTap: () {
|
onTap: () {},
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => BasePage(mainScreen: "map")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
ListTile(
|
|
||||||
title: const Text('Trip Overview'),
|
|
||||||
leading: const Icon(Icons.list),
|
|
||||||
trailing: ElevatedButton(
|
trailing: ElevatedButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
@ -86,7 +80,23 @@ class _BasePageState extends State<BasePage> {
|
|||||||
child: const Text('New'),
|
child: const Text('New'),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(child: TripsOverview()),
|
|
||||||
|
// Adds 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.
|
||||||
|
Expanded(
|
||||||
|
child: TripsOverview(trips: trips),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => const NewTripPage()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Clear trips'),
|
||||||
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('How to use'),
|
title: const Text('How to use'),
|
||||||
@ -101,6 +111,7 @@ class _BasePageState extends State<BasePage> {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// settings in the bottom of the drawer
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Settings'),
|
title: const Text('Settings'),
|
||||||
leading: const Icon(Icons.settings),
|
leading: const Icon(Icons.settings),
|
||||||
@ -113,7 +124,6 @@ class _BasePageState extends State<BasePage> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// settings in the bottom of the drawer
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -121,3 +131,9 @@ class _BasePageState extends State<BasePage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Future<Trip> getFirstTrip (Future<List<Trip>> trips) async {
|
||||||
|
List<Trip> tripsf = await trips;
|
||||||
|
return tripsf[0];
|
||||||
|
}
|
@ -17,4 +17,3 @@ class App extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
Widget Greeter (ThemeData theme, {bool full = false}) {
|
Widget Greeter(ThemeData theme, {bool full = false}) {
|
||||||
String greeterText = "";
|
String greeterText = "";
|
||||||
try {
|
try {
|
||||||
String cityName = getCityName();
|
String cityName = getCityName();
|
||||||
|
@ -32,7 +32,9 @@ class _LandmarkCardState extends State<LandmarkCard> {
|
|||||||
// force a fixed width
|
// force a fixed width
|
||||||
width: 160,
|
width: 160,
|
||||||
child: Image.network(
|
child: Image.network(
|
||||||
widget.landmark.imageURL,
|
widget.landmark.imageURL!,
|
||||||
|
errorBuilder: (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
|
// cover the whole container meaning the image will be cropped
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
|
@ -1,29 +1,34 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:fast_network_navigation/modules/landmark_card.dart';
|
import 'package:fast_network_navigation/modules/landmark_card.dart';
|
||||||
import 'package:fast_network_navigation/structs/landmark.dart';
|
import 'package:fast_network_navigation/structs/landmark.dart';
|
||||||
import 'package:fast_network_navigation/utils/get_landmarks.dart';
|
|
||||||
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class loadLandmarksOverview extends StatefulWidget {
|
class LandmarksOverview extends StatefulWidget {
|
||||||
const loadLandmarksOverview({super.key});
|
final Future<Trip>? trip;
|
||||||
|
const LandmarksOverview({super.key, this.trip});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<loadLandmarksOverview> createState() => _loadLandmarksOverviewState();
|
State<LandmarksOverview> createState() => _LandmarksOverviewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _loadLandmarksOverviewState extends State<loadLandmarksOverview> {
|
class _LandmarksOverviewState extends State<LandmarksOverview> {
|
||||||
final Future<List<Landmark>> _landmarks = fetchLandmarks();
|
// final Future<List<Landmark>> _landmarks = fetchLandmarks();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final Future<LinkedList<Landmark>> _landmarks = getLandmarks(widget.trip);
|
||||||
return DefaultTextStyle(
|
return DefaultTextStyle(
|
||||||
style: Theme.of(context).textTheme.displayMedium!,
|
style: Theme.of(context).textTheme.displayMedium!,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
child: FutureBuilder<List<Landmark>>(
|
child: FutureBuilder<LinkedList<Landmark>>(
|
||||||
future: _landmarks,
|
future: _landmarks,
|
||||||
builder: (BuildContext context, AsyncSnapshot<List<Landmark>> snapshot) {
|
builder: (BuildContext context, AsyncSnapshot<LinkedList<Landmark>> snapshot) {
|
||||||
List<Widget> children;
|
List<Widget> children;
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
children = [landmarksWithSteps(snapshot.data!)];
|
children = [landmarksWithSteps(snapshot.data!)];
|
||||||
@ -42,13 +47,10 @@ class _loadLandmarksOverviewState extends State<loadLandmarksOverview> {
|
|||||||
} else {
|
} else {
|
||||||
children = [Center(child: CircularProgressIndicator())];
|
children = [Center(child: CircularProgressIndicator())];
|
||||||
}
|
}
|
||||||
return Padding(
|
return Center(
|
||||||
padding: const EdgeInsets.all(10),
|
child: Column(
|
||||||
child: Center(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
child: Column(
|
children: children,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: children,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -57,12 +59,12 @@ class _loadLandmarksOverviewState extends State<loadLandmarksOverview> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget landmarksWithSteps(List<Landmark> landmarks) {
|
Widget landmarksWithSteps(LinkedList<Landmark> landmarks) {
|
||||||
List<Widget> children = [];
|
List<Widget> children = [];
|
||||||
for (int i = 0; i < landmarks.length; i++) {
|
for (Landmark landmark in landmarks) {
|
||||||
children.add(LandmarkCard(landmarks[i]));
|
children.add(LandmarkCard(landmark));
|
||||||
if (i < landmarks.length - 1) {
|
if (landmark.next != null) {
|
||||||
Widget step = stepBetweenLandmarks(landmarks[i], landmarks[i + 1]);
|
Widget step = stepBetweenLandmarks(landmark, landmark.next!);
|
||||||
children.add(step);
|
children.add(step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,3 +112,8 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<LinkedList<Landmark>> getLandmarks (Future<Trip>? trip) async {
|
||||||
|
Trip tripf = await trip!;
|
||||||
|
return tripf.landmarks;
|
||||||
|
}
|
||||||
|
@ -1,9 +1,18 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
import 'package:fast_network_navigation/structs/landmark.dart';
|
import 'package:fast_network_navigation/structs/landmark.dart';
|
||||||
import 'package:fast_network_navigation/utils/get_landmarks.dart';
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
|
|
||||||
class MapWidget extends StatefulWidget {
|
class MapWidget extends StatefulWidget {
|
||||||
|
|
||||||
|
final Future<Trip>? trip;
|
||||||
|
|
||||||
|
MapWidget({
|
||||||
|
this.trip
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MapWidget> createState() => _MapWidgetState();
|
State<MapWidget> createState() => _MapWidgetState();
|
||||||
}
|
}
|
||||||
@ -31,7 +40,8 @@ class _MapWidgetState extends State<MapWidget> {
|
|||||||
|
|
||||||
void drawLandmarks() async {
|
void drawLandmarks() async {
|
||||||
// (re)draws landmarks on the map
|
// (re)draws landmarks on the map
|
||||||
List<Landmark> landmarks = await fetchLandmarks();
|
Trip? trip = await widget.trip;
|
||||||
|
LinkedList<Landmark> landmarks = trip!.landmarks;
|
||||||
setState(() {
|
setState(() {
|
||||||
for (Landmark landmark in landmarks) {
|
for (Landmark landmark in landmarks) {
|
||||||
markers.add(Marker(
|
markers.add(Marker(
|
||||||
@ -55,4 +65,3 @@ class _MapWidgetState extends State<MapWidget> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,19 +2,21 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:fast_network_navigation/layout.dart';
|
import 'package:fast_network_navigation/layout.dart';
|
||||||
import 'package:fast_network_navigation/structs/trip.dart';
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
import 'package:fast_network_navigation/utils/get_trips.dart';
|
|
||||||
|
|
||||||
|
|
||||||
class TripsOverview extends StatefulWidget {
|
class TripsOverview extends StatefulWidget {
|
||||||
|
final Future<List<Trip>> trips;
|
||||||
const TripsOverview({super.key});
|
const TripsOverview({
|
||||||
|
super.key,
|
||||||
|
required this.trips,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<TripsOverview> createState() => _TripsOverviewState();
|
State<TripsOverview> createState() => _TripsOverviewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TripsOverviewState extends State<TripsOverview> {
|
class _TripsOverviewState extends State<TripsOverview> {
|
||||||
final Future<List<Trip>> _trips = loadTrips();
|
// final Future<List<Trip>> _trips = loadTrips();
|
||||||
|
|
||||||
|
|
||||||
Widget listBuild (BuildContext context, AsyncSnapshot<List<Trip>> snapshot) {
|
Widget listBuild (BuildContext context, AsyncSnapshot<List<Trip>> snapshot) {
|
||||||
@ -23,12 +25,12 @@ class _TripsOverviewState extends State<TripsOverview> {
|
|||||||
children = List<Widget>.generate(snapshot.data!.length, (index) {
|
children = List<Widget>.generate(snapshot.data!.length, (index) {
|
||||||
Trip trip = snapshot.data![index];
|
Trip trip = snapshot.data![index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
title: Text("Trip to ${trip.cityName} (${trip.landmarks.length} stops)"),
|
title: Text("Trip to ${trip.cityName}"),
|
||||||
leading: Icon(Icons.pin_drop),
|
leading: Icon(Icons.pin_drop),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => BasePage(mainScreen: "map") //, trip: trip)
|
builder: (context) => BasePage(mainScreen: "map", trip: trip)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -58,7 +60,7 @@ class _TripsOverviewState extends State<TripsOverview> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FutureBuilder(
|
return FutureBuilder(
|
||||||
future: _trips,
|
future: widget.trips,
|
||||||
builder: listBuild,
|
builder: listBuild,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:fast_network_navigation/modules/greeter.dart';
|
import 'package:fast_network_navigation/modules/greeter.dart';
|
||||||
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:sliding_up_panel/sliding_up_panel.dart';
|
import 'package:sliding_up_panel/sliding_up_panel.dart';
|
||||||
|
|
||||||
@ -8,6 +9,12 @@ import 'package:fast_network_navigation/modules/map.dart';
|
|||||||
|
|
||||||
|
|
||||||
class NavigationOverview extends StatefulWidget {
|
class NavigationOverview extends StatefulWidget {
|
||||||
|
final Future<Trip> trip;
|
||||||
|
|
||||||
|
NavigationOverview({
|
||||||
|
required this.trip
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<NavigationOverview> createState() => _NavigationOverviewState();
|
State<NavigationOverview> createState() => _NavigationOverviewState();
|
||||||
}
|
}
|
||||||
@ -23,7 +30,7 @@ class _NavigationOverviewState extends State<NavigationOverview> {
|
|||||||
renderPanelSheet: false,
|
renderPanelSheet: false,
|
||||||
panel: _floatingPanel(theme),
|
panel: _floatingPanel(theme),
|
||||||
collapsed: _floatingCollapsed(theme),
|
collapsed: _floatingCollapsed(theme),
|
||||||
body: MapWidget()
|
body: MapWidget(trip: widget.trip)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +65,7 @@ class _NavigationOverviewState extends State<NavigationOverview> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Greeter(theme, full: true),
|
Greeter(theme, full: true),
|
||||||
loadLandmarksOverview(),
|
LandmarksOverview(trip: widget.trip),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -51,13 +51,7 @@ class ImportanceSliders extends StatefulWidget {
|
|||||||
|
|
||||||
class _ImportanceSlidersState extends State<ImportanceSliders> {
|
class _ImportanceSlidersState extends State<ImportanceSliders> {
|
||||||
|
|
||||||
final UserPreferences _prefs = UserPreferences();
|
UserPreferences _prefs = UserPreferences();
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
_prefs.load();
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Card> _createSliders() {
|
List<Card> _createSliders() {
|
||||||
List<Card> sliders = [];
|
List<Card> sliders = [];
|
||||||
@ -89,6 +83,7 @@ class _ImportanceSlidersState extends State<ImportanceSliders> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
||||||
return Column(children: _createSliders());
|
return Column(children: _createSliders());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,56 +1,69 @@
|
|||||||
class Landmark {
|
import 'dart:collection';
|
||||||
final String name;
|
import 'dart:convert';
|
||||||
final List location;
|
|
||||||
final LandmarkType type;
|
|
||||||
final String imageURL;
|
|
||||||
// final String description;
|
|
||||||
// final Duration duration;
|
|
||||||
// final bool visited;
|
|
||||||
|
|
||||||
const Landmark({
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
final class Landmark extends LinkedListEntry<Landmark>{
|
||||||
|
// A linked node of a list of Landmarks
|
||||||
|
final String uuid;
|
||||||
|
final String name;
|
||||||
|
final List<double> location;
|
||||||
|
final LandmarkType type;
|
||||||
|
final bool? isSecondary;
|
||||||
|
|
||||||
|
// description to be shown in the overview
|
||||||
|
final String? imageURL;
|
||||||
|
final String? description;
|
||||||
|
final Duration? duration;
|
||||||
|
final bool? visited;
|
||||||
|
|
||||||
|
// Next node
|
||||||
|
// final Landmark? next;
|
||||||
|
final Duration? tripTime;
|
||||||
|
|
||||||
|
Landmark({
|
||||||
|
required this.uuid,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.location,
|
required this.location,
|
||||||
required this.type,
|
required this.type,
|
||||||
this.imageURL = 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg',
|
this.isSecondary,
|
||||||
// required this.description,
|
|
||||||
// required this.duration,
|
this.imageURL,
|
||||||
// required this.visited,
|
this.description,
|
||||||
|
this.duration,
|
||||||
|
this.visited,
|
||||||
|
|
||||||
|
// this.next,
|
||||||
|
this.tripTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Landmark.fromJson(Map<String, dynamic> json) {
|
factory Landmark.fromJson(Map<String, dynamic> json) {
|
||||||
return switch (json) {
|
if (json
|
||||||
{
|
case { // automatically match all the non-optionals and cast them to the right type
|
||||||
'loc': List location,
|
'uuid': String uuid,
|
||||||
'name': String name,
|
'name': String name,
|
||||||
'type': String type,
|
'location': List<double> location,
|
||||||
// 'description': String description,
|
'type': LandmarkType type,
|
||||||
// 'duration': int duration,
|
}) {
|
||||||
// 'visited': bool visited
|
// parse the rest separately, they could be missing
|
||||||
|
final isSecondary = json['is_secondary'] as bool?;
|
||||||
|
final imageURL = json['image_url'] as String?;
|
||||||
|
final description = json['description'] as String?;
|
||||||
|
final duration = json['duration'] as Duration?;
|
||||||
|
final visited = json['visited'] as bool?;
|
||||||
|
|
||||||
} =>
|
return Landmark(
|
||||||
Landmark(
|
uuid: uuid, name: name, location: location, type: type, isSecondary: isSecondary, imageURL: imageURL, description: description, duration: duration, visited: visited);
|
||||||
name: name,
|
} else {
|
||||||
location: location,
|
throw FormatException('Invalid JSON: $json');
|
||||||
type: LandmarkType(name: type)
|
}
|
||||||
// description: description,
|
|
||||||
// duration: Duration(minutes: duration),
|
|
||||||
// visited: visited
|
|
||||||
),
|
|
||||||
_ => throw const FormatException('Failed to load destination.'),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
@override
|
||||||
return {
|
bool operator ==(Object other) {
|
||||||
'name': name,
|
return other is Landmark && uuid == other.uuid;
|
||||||
'location': location,
|
}
|
||||||
'type': type.name,
|
|
||||||
// 'description': description,
|
|
||||||
// 'duration': duration.inMinutes,
|
|
||||||
// 'visited': visited
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -67,3 +80,12 @@ class LandmarkType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Helper
|
||||||
|
(Landmark, String?) getLandmarkFromPrefs(SharedPreferences prefs, String uuid) {
|
||||||
|
String? content = prefs.getString('landmark_$uuid');
|
||||||
|
Map<String, dynamic> json = jsonDecode(content!);
|
||||||
|
String? nextUUID = json['next_uuid'];
|
||||||
|
return (Landmark.fromJson(json), nextUUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
46
frontend/lib/structs/linked_landmarks.dart
Normal file
46
frontend/lib/structs/linked_landmarks.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
// import "package:fast_network_navigation/structs/landmark.dart";
|
||||||
|
|
||||||
|
// class Linked<Landmark> {
|
||||||
|
// Landmark? head;
|
||||||
|
|
||||||
|
// Linked();
|
||||||
|
|
||||||
|
// // class methods
|
||||||
|
// bool get isEmpty => head == null;
|
||||||
|
|
||||||
|
// // Add a new node to the end of the list
|
||||||
|
// void add(Landmark value) {
|
||||||
|
// if (isEmpty) {
|
||||||
|
// // If the list is empty, set the new node as the head
|
||||||
|
// head = value;
|
||||||
|
// } else {
|
||||||
|
// Landmark? current = head;
|
||||||
|
// while (current!.next != null) {
|
||||||
|
// // Traverse the list to find the last node
|
||||||
|
// current = current.next;
|
||||||
|
// }
|
||||||
|
// current.next = value; // Set the new node as the next node of the last node
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Remove the first node with the given value
|
||||||
|
// void remove(Landmark value) {
|
||||||
|
// if (isEmpty) return;
|
||||||
|
|
||||||
|
// // If the value is in the head node, update the head to the next node
|
||||||
|
// if (head! == value) {
|
||||||
|
// head = head.next;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var current = head;
|
||||||
|
// while (current!.next != null) {
|
||||||
|
// if (current.next! == value) {
|
||||||
|
// // If the value is found in the next node, skip the next node
|
||||||
|
// current.next = current.next.next;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// current = current.next;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
@ -67,16 +67,16 @@ class UserPreferences {
|
|||||||
|
|
||||||
|
|
||||||
void save() async {
|
void save() async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
|
||||||
for (SinglePreference pref in preferences) {
|
for (SinglePreference pref in preferences) {
|
||||||
prefs.setInt(pref.key, pref.value);
|
sharedPrefs.setInt(pref.key, pref.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void load() async {
|
void load() async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences sharedPrefs = await SharedPreferences.getInstance();
|
||||||
for (SinglePreference pref in preferences) {
|
for (SinglePreference pref in preferences) {
|
||||||
pref.value = prefs.getInt(pref.key) ?? 0;
|
pref.value = sharedPrefs.getInt(pref.key) ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,25 +1,49 @@
|
|||||||
// Represents a collection of landmarks that represent a journey
|
// Represents a collection of landmarks that represent a journey
|
||||||
// Different instances of a Trip can be saved and loaded by the user
|
// Different instances of a Trip can be saved and loaded by the user
|
||||||
|
|
||||||
|
import 'dart:collection';
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:fast_network_navigation/structs/landmark.dart';
|
import 'package:fast_network_navigation/structs/landmark.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class Trip {
|
class Trip {
|
||||||
final String uuid;
|
final String uuid;
|
||||||
final String cityName;
|
final String cityName;
|
||||||
final List<Landmark> landmarks;
|
// TODO: cityName should be inferred from coordinates of the Landmarks
|
||||||
|
final LinkedList<Landmark> landmarks;
|
||||||
|
// could be empty as well
|
||||||
|
|
||||||
|
Trip({
|
||||||
Trip({required this.uuid, required this.cityName, required this.landmarks});
|
required this.uuid,
|
||||||
|
required this.cityName,
|
||||||
|
required this.landmarks,
|
||||||
|
});
|
||||||
|
|
||||||
factory Trip.fromJson(Map<String, dynamic> json) {
|
factory Trip.fromJson(Map<String, dynamic> json) {
|
||||||
List<Landmark> landmarks = [];
|
|
||||||
for (var landmark in json['landmarks']) {
|
|
||||||
landmarks.add(Landmark.fromJson(landmark));
|
|
||||||
}
|
|
||||||
return Trip(
|
return Trip(
|
||||||
uuid: json['uuid'],
|
uuid: json['uuid'],
|
||||||
cityName: json['cityName'],
|
cityName: json['cityName'],
|
||||||
landmarks: landmarks,
|
landmarks: LinkedList()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
factory Trip.fromPrefs(SharedPreferences prefs, String uuid) {
|
||||||
|
String? content = prefs.getString('trip_$uuid');
|
||||||
|
Map<String, dynamic> json = jsonDecode(content!);
|
||||||
|
Trip trip = Trip.fromJson(json);
|
||||||
|
String? firstUUID = json['entry_uuid'];
|
||||||
|
appendLandmarks(trip.landmarks, prefs, firstUUID);
|
||||||
|
return trip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper
|
||||||
|
|
||||||
|
appendLandmarks(LinkedList<Landmark> landmarks, SharedPreferences prefs, String? firstUUID) {
|
||||||
|
while (firstUUID != null) {
|
||||||
|
var (head, nextUUID) = getLandmarkFromPrefs(prefs, firstUUID);
|
||||||
|
landmarks.add(head);
|
||||||
|
firstUUID = nextUUID;
|
||||||
|
}
|
||||||
}
|
}
|
52
frontend/lib/utils/fetch_landmarks.dart
Normal file
52
frontend/lib/utils/fetch_landmarks.dart
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
// import "package:fast_network_navigation/structs/landmark.dart";
|
||||||
|
// import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
|
||||||
|
// Future<List<Landmark>> 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<Landmark> landmarks = [
|
||||||
|
// // 48°51′29.6″N 2°17′40.2″E
|
||||||
|
// Landmark(
|
||||||
|
// name: "Eiffel Tower",
|
||||||
|
// location: [48.51296, 2.17402],
|
||||||
|
// 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"
|
||||||
|
// ),
|
||||||
|
// Landmark(
|
||||||
|
// 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"
|
||||||
|
// ),
|
||||||
|
// Landmark(
|
||||||
|
// 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"
|
||||||
|
// ),
|
||||||
|
// Landmark(
|
||||||
|
// name: "Pont-des-arts",
|
||||||
|
// location: [48.5130, 2.2015],
|
||||||
|
// 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"),
|
||||||
|
// Landmark(
|
||||||
|
// name: "Panthéon",
|
||||||
|
// location: [48.5046, 2.2046],
|
||||||
|
// 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"
|
||||||
|
// ),
|
||||||
|
// ];
|
||||||
|
// // sleep 10 seconds
|
||||||
|
// await Future.delayed(Duration(seconds: 5));
|
||||||
|
// return landmarks;
|
||||||
|
// // } else {
|
||||||
|
// // // If the server did not return a 200 OK response,
|
||||||
|
// // // then throw an exception.
|
||||||
|
// // throw Exception('Failed to load destination');
|
||||||
|
// // }
|
||||||
|
// }
|
@ -1,52 +0,0 @@
|
|||||||
import "package:fast_network_navigation/structs/landmark.dart";
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
|
|
||||||
|
|
||||||
Future<List<Landmark>> 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<Landmark> landmarks = [
|
|
||||||
// 48°51′29.6″N 2°17′40.2″E
|
|
||||||
Landmark(
|
|
||||||
name: "Eiffel Tower",
|
|
||||||
location: [48.51296, 2.17402],
|
|
||||||
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"
|
|
||||||
),
|
|
||||||
Landmark(
|
|
||||||
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"
|
|
||||||
),
|
|
||||||
Landmark(
|
|
||||||
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"
|
|
||||||
),
|
|
||||||
Landmark(
|
|
||||||
name: "Pont-des-arts",
|
|
||||||
location: [48.5130, 2.2015],
|
|
||||||
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"),
|
|
||||||
Landmark(
|
|
||||||
name: "Panthéon",
|
|
||||||
location: [48.5046, 2.2046],
|
|
||||||
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"
|
|
||||||
),
|
|
||||||
];
|
|
||||||
// sleep 10 seconds
|
|
||||||
await Future.delayed(Duration(seconds: 5));
|
|
||||||
return landmarks;
|
|
||||||
// } else {
|
|
||||||
// // If the server did not return a 200 OK response,
|
|
||||||
// // then throw an exception.
|
|
||||||
// throw Exception('Failed to load destination');
|
|
||||||
// }
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
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<List<Trip>> loadTrips() async {
|
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
|
||||||
|
|
||||||
Set<String> keys = prefs.getKeys();
|
|
||||||
List<Trip> 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;
|
|
||||||
}
|
|
44
frontend/lib/utils/load_trips.dart
Normal file
44
frontend/lib/utils/load_trips.dart
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:fast_network_navigation/structs/linked_landmarks.dart';
|
||||||
|
import 'package:fast_network_navigation/structs/trip.dart';
|
||||||
|
import 'package:fast_network_navigation/structs/landmark.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
Future<List<Trip>> loadTrips() async {
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
List<Trip> trips = [];
|
||||||
|
Set<String> keys = prefs.getKeys();
|
||||||
|
for (String key in keys) {
|
||||||
|
if (key.startsWith('trip_')) {
|
||||||
|
String uuid = key.replaceFirst('trip_', '');
|
||||||
|
trips.add(Trip.fromPrefs(prefs, uuid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trips.isEmpty) {
|
||||||
|
String now = DateTime.now().toString();
|
||||||
|
trips.add(
|
||||||
|
Trip(uuid: '1', cityName: 'Paris (generated $now)', landmarks: LinkedList<Landmark>())
|
||||||
|
);
|
||||||
|
// 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")),
|
||||||
|
// ]));
|
||||||
|
// trips.add(Trip(uuid: "2", cityName: "Vienna", landmarks: []));
|
||||||
|
// trips.add(Trip(uuid: "3", cityName: "London", landmarks: []));
|
||||||
|
// trips.add(Trip(uuid: "4", cityName: "Madrid", landmarks: []));
|
||||||
|
// trips.add(Trip(uuid: "5", cityName: "Tokyo", landmarks: []));
|
||||||
|
// trips.add(Trip(uuid: "6", cityName: "New York", landmarks: []));
|
||||||
|
// trips.add(Trip(uuid: "7", cityName: "Los Angeles", landmarks: []));
|
||||||
|
// trips.add(Trip(uuid: "8", cityName: "Zurich", landmarks: []));
|
||||||
|
// trips.add(Trip(uuid: "9", cityName: "Orschwiller", landmarks: []));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return trips;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user