overhaul using a trip struct that notifies its ui dependencies
This commit is contained in:
parent
5748630b99
commit
b1fd0f47fa
@ -1,3 +1,6 @@
|
|||||||
|
import 'dart:collection';
|
||||||
|
|
||||||
|
import 'package:anyway/structs/landmark.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:anyway/constants.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
|
// A side drawer is used to switch between pages
|
||||||
class BasePage extends StatefulWidget {
|
class BasePage extends StatefulWidget {
|
||||||
final String mainScreen;
|
final String mainScreen;
|
||||||
final Future<Trip>? trip;
|
final Trip? trip;
|
||||||
|
|
||||||
const BasePage({
|
const BasePage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.mainScreen,
|
required this.mainScreen,
|
||||||
this.trip
|
this.trip,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -53,13 +56,13 @@ class _BasePageState extends State<BasePage> {
|
|||||||
children: [
|
children: [
|
||||||
DrawerHeader(
|
DrawerHeader(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(colors: [Colors.cyan, theme.primaryColor])
|
gradient: LinearGradient(colors: [Colors.red, Colors.yellow])
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text(
|
child: Text(
|
||||||
APP_NAME,
|
APP_NAME,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.grey[800],
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
),
|
),
|
||||||
@ -129,9 +132,54 @@ class _BasePageState extends State<BasePage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This function is used to get the first trip from a list of trips
|
||||||
|
// TODO: Implement this function
|
||||||
Future<Trip> getFirstTrip (Future<List<Trip>> trips) async {
|
Trip getFirstTrip(Future<List<Trip>> trips) {
|
||||||
List<Trip> tripsf = await trips;
|
Trip t1 = Trip(uuid: '1', landmarks: LinkedList<Landmark>());
|
||||||
return tripsf[0];
|
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;
|
||||||
}
|
}
|
@ -13,7 +13,7 @@ class App extends StatelessWidget {
|
|||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: APP_NAME,
|
title: APP_NAME,
|
||||||
home: BasePage(mainScreen: "map"),
|
home: BasePage(mainScreen: "map"),
|
||||||
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.green),
|
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.red[600]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,10 @@ import 'package:anyway/structs/trip.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class Greeter extends StatefulWidget {
|
class Greeter extends StatefulWidget {
|
||||||
final Future<Trip> trip;
|
final Trip trip;
|
||||||
final bool standalone;
|
|
||||||
|
|
||||||
Greeter({
|
Greeter({
|
||||||
required this.standalone,
|
required this.trip,
|
||||||
required this.trip
|
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -18,56 +16,67 @@ class Greeter extends StatefulWidget {
|
|||||||
|
|
||||||
|
|
||||||
class _GreeterState extends State<Greeter> {
|
class _GreeterState extends State<Greeter> {
|
||||||
Widget greeterBuild (BuildContext context, AsyncSnapshot<Trip> snapshot) {
|
Widget greeterBuilder (BuildContext context, Widget? child) {
|
||||||
ThemeData theme = Theme.of(context);
|
ThemeData theme = Theme.of(context);
|
||||||
Widget topGreeter;
|
Widget topGreeter;
|
||||||
|
if (widget.trip.landmarks.length > 1) {
|
||||||
|
topGreeter = FutureBuilder(
|
||||||
|
future: widget.trip.cityName,
|
||||||
|
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
|
||||||
if (snapshot.hasData) {
|
if (snapshot.hasData) {
|
||||||
topGreeter = Padding(
|
return Text(
|
||||||
padding: const EdgeInsets.only(top: 20, bottom: 20),
|
'Welcome to ${snapshot.data}!',
|
||||||
child: Text(
|
|
||||||
'Welcome to ${snapshot.data?.cityName}!',
|
|
||||||
style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24),
|
style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24),
|
||||||
)
|
|
||||||
);
|
);
|
||||||
} else if (snapshot.hasError) {
|
} else if (snapshot.hasError) {
|
||||||
topGreeter = const Padding(
|
return const Text('Welcome to your trip!');
|
||||||
padding: EdgeInsets.only(top: 20, bottom: 20),
|
} else {
|
||||||
child: Text('Error while fetching trip')
|
return const Text('Welcome to ...');
|
||||||
|
}
|
||||||
|
}
|
||||||
);
|
);
|
||||||
} else {
|
} 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
|
// Show a linear loader at the bottom and an info message above
|
||||||
topGreeter = Column(
|
topGreeter = Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
FutureBuilder(
|
||||||
padding: const EdgeInsets.only(top: 20, bottom: 20),
|
future: widget.trip.cityName,
|
||||||
child: const Text('Generating your trip...', style: TextStyle(fontSize: 20),)
|
builder: (BuildContext context, AsyncSnapshot<String> 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(
|
return Center(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Padding(padding: EdgeInsets.only(top: 24.0)),
|
// Padding(padding: EdgeInsets.only(top: 20)),
|
||||||
topGreeter,
|
topGreeter,
|
||||||
bottomGreeter,
|
Padding(
|
||||||
Padding(padding: EdgeInsets.only(bottom: 24.0)),
|
padding: EdgeInsets.all(20),
|
||||||
|
child: bottomGreeter
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Widget bottomGreeter = const Text(
|
Widget bottomGreeter = const Text(
|
||||||
"Busy day ahead? Here is how to make the most of it!",
|
"Busy day ahead? Here is how to make the most of it!",
|
||||||
@ -79,9 +88,9 @@ class _GreeterState extends State<Greeter> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FutureBuilder(
|
return ListenableBuilder(
|
||||||
future: widget.trip,
|
listenable: widget.trip,
|
||||||
builder: greeterBuild,
|
builder: greeterBuilder,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:anyway/structs/landmark.dart';
|
import 'package:anyway/structs/landmark.dart';
|
||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
||||||
@ -31,9 +32,10 @@ class _LandmarkCardState extends State<LandmarkCard> {
|
|||||||
height: double.infinity,
|
height: double.infinity,
|
||||||
// force a fixed width
|
// force a fixed width
|
||||||
width: 160,
|
width: 160,
|
||||||
child: Image.network(
|
child: CachedNetworkImage(
|
||||||
widget.landmark.imageURL ?? '',
|
imageUrl: widget.landmark.imageURL ?? '',
|
||||||
errorBuilder: (context, error, stackTrace) => Icon(Icons.question_mark_outlined),
|
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
|
// 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,17 +1,17 @@
|
|||||||
import 'dart:collection';
|
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/modules/landmark_card.dart';
|
||||||
import 'package:anyway/structs/landmark.dart';
|
import 'package:anyway/structs/landmark.dart';
|
||||||
|
|
||||||
import 'package:anyway/structs/trip.dart';
|
import 'package:anyway/structs/trip.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class LandmarksOverview extends StatefulWidget {
|
class LandmarksOverview extends StatefulWidget {
|
||||||
final Future<Trip>? trip;
|
final Trip? trip;
|
||||||
const LandmarksOverview({super.key, this.trip});
|
const LandmarksOverview({super.key, this.trip});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -23,18 +23,17 @@ class _LandmarksOverviewState extends State<LandmarksOverview> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final Future<LinkedList<Landmark>> _landmarks = getLandmarks(widget.trip);
|
return ListenableBuilder(//<LinkedList<Landmark>>
|
||||||
return DefaultTextStyle(
|
listenable: widget.trip!,
|
||||||
style: Theme.of(context).textTheme.displayMedium!,
|
builder: (BuildContext context, Widget? child) {
|
||||||
textAlign: TextAlign.center,
|
Trip trip = widget.trip!;
|
||||||
child: FutureBuilder<LinkedList<Landmark>>(
|
log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks");
|
||||||
future: _landmarks,
|
|
||||||
builder: (BuildContext context, AsyncSnapshot<LinkedList<Landmark>> snapshot) {
|
|
||||||
List<Widget> children;
|
List<Widget> children;
|
||||||
if (snapshot.hasData) {
|
if (trip.uuid == 'pending') {
|
||||||
children = [landmarksWithSteps(snapshot.data!), saveButton()];
|
// the trip is still being fetched from the api
|
||||||
} else if (snapshot.hasError) {
|
children = [Center(child: CircularProgressIndicator())];
|
||||||
children = <Widget>[
|
} else if (trip.uuid == 'error') {
|
||||||
|
children = [
|
||||||
const Icon(
|
const Icon(
|
||||||
Icons.error_outline,
|
Icons.error_outline,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
@ -42,20 +41,25 @@ class _LandmarksOverviewState extends State<LandmarksOverview> {
|
|||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 16),
|
padding: const EdgeInsets.only(top: 16),
|
||||||
child: Text('Error: ${snapshot.error}', style: TextStyle(fontSize: 12)),
|
child: Text('Error: ${trip.cityName}'),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
children = [Center(child: CircularProgressIndicator())];
|
if (trip.landmarks.length <= 1) {
|
||||||
|
children = [
|
||||||
|
const Text("No landmarks in this trip"),
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
children = [
|
||||||
|
landmarksWithSteps(trip.landmarks),
|
||||||
|
saveButton(),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
return Center(
|
}
|
||||||
child: Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: children,
|
children: children,
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Widget saveButton() => ElevatedButton(
|
Widget saveButton() => ElevatedButton(
|
||||||
@ -100,7 +104,7 @@ Widget landmarksWithSteps(LinkedList<Landmark> landmarks) {
|
|||||||
);
|
);
|
||||||
lkey++;
|
lkey++;
|
||||||
if (landmark.next != null) {
|
if (landmark.next != null) {
|
||||||
Widget step = stepBetweenLandmarks(landmark, landmark.next!);
|
Widget step = stepBetweenLandmarks(landmark);
|
||||||
children.add(step);
|
children.add(step);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -111,7 +115,7 @@ Widget landmarksWithSteps(LinkedList<Landmark> landmarks) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Widget stepBetweenLandmarks(Landmark before, Landmark after) {
|
Widget stepBetweenLandmarks(Landmark landmark) {
|
||||||
// This is a simple widget that draws a line between landmark-cards
|
// This is a simple widget that draws a line between landmark-cards
|
||||||
// It's a vertical dotted line
|
// 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
|
// 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(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.directions_walk),
|
Icon(Icons.directions_walk),
|
||||||
Text("5 min", style: TextStyle(fontSize: 10)),
|
Text("${landmark.tripTime} min", style: TextStyle(fontSize: 10)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
@ -149,8 +153,5 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<LinkedList<Landmark>> getLandmarks (Future<Trip>? trip) async {
|
|
||||||
Trip tripf = await trip!;
|
|
||||||
return tripf.landmarks;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import 'package:the_widget_marker/the_widget_marker.dart';
|
|||||||
|
|
||||||
class MapWidget extends StatefulWidget {
|
class MapWidget extends StatefulWidget {
|
||||||
|
|
||||||
final Future<Trip>? trip;
|
final Trip? trip;
|
||||||
|
|
||||||
MapWidget({
|
MapWidget({
|
||||||
this.trip
|
this.trip
|
||||||
@ -31,8 +31,7 @@ class _MapWidgetState extends State<MapWidget> {
|
|||||||
|
|
||||||
void _onMapCreated(GoogleMapController controller) async {
|
void _onMapCreated(GoogleMapController controller) async {
|
||||||
mapController = controller;
|
mapController = controller;
|
||||||
Trip? trip = await widget.trip;
|
List<double>? newLocation = widget.trip?.landmarks.first.location;
|
||||||
List<double>? newLocation = trip?.landmarks.first.location;
|
|
||||||
if (newLocation != null) {
|
if (newLocation != null) {
|
||||||
CameraUpdate update = CameraUpdate.newLatLng(LatLng(newLocation[0], newLocation[1]));
|
CameraUpdate update = CameraUpdate.newLatLng(LatLng(newLocation[0], newLocation[1]));
|
||||||
controller.moveCamera(update);
|
controller.moveCamera(update);
|
||||||
@ -48,8 +47,7 @@ class _MapWidgetState extends State<MapWidget> {
|
|||||||
|
|
||||||
void drawLandmarks() async {
|
void drawLandmarks() async {
|
||||||
// (re)draws landmarks on the map
|
// (re)draws landmarks on the map
|
||||||
Trip? trip = await widget.trip;
|
LinkedList<Landmark>? landmarks = widget.trip?.landmarks;
|
||||||
LinkedList<Landmark>? landmarks = trip?.landmarks;
|
|
||||||
if (landmarks != null){
|
if (landmarks != null){
|
||||||
for (Landmark landmark in landmarks) {
|
for (Landmark landmark in landmarks) {
|
||||||
markers.add(Marker(
|
markers.add(Marker(
|
||||||
|
@ -30,7 +30,7 @@ class _TripsOverviewState extends State<TripsOverview> {
|
|||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).push(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => BasePage(mainScreen: "map", trip: Future.value(trip))
|
builder: (context) => BasePage(mainScreen: "map", trip: trip)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -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/layout.dart';
|
||||||
import 'package:anyway/structs/preferences.dart';
|
|
||||||
import 'package:anyway/utils/fetch_trip.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";
|
import "package:anyway/structs/trip.dart";
|
||||||
|
|
||||||
|
|
||||||
@ -64,7 +66,16 @@ class _NewTripPageState extends State<NewTripPage> {
|
|||||||
double.parse(lonController.text)
|
double.parse(lonController.text)
|
||||||
];
|
];
|
||||||
Future<UserPreferences> preferences = loadUserPreferences();
|
Future<UserPreferences> preferences = loadUserPreferences();
|
||||||
Future<Trip>? 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(
|
Navigator.of(context).push(
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => BasePage(mainScreen: "map", trip: trip)
|
builder: (context) => BasePage(mainScreen: "map", trip: trip)
|
||||||
|
@ -10,10 +10,10 @@ import 'package:anyway/modules/greeter.dart';
|
|||||||
|
|
||||||
|
|
||||||
class NavigationOverview extends StatefulWidget {
|
class NavigationOverview extends StatefulWidget {
|
||||||
final Future<Trip> trip;
|
final Trip trip;
|
||||||
|
|
||||||
NavigationOverview({
|
NavigationOverview({
|
||||||
required this.trip
|
required this.trip,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -27,53 +27,56 @@ class _NavigationOverviewState extends State<NavigationOverview> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SlidingUpPanel(
|
return SlidingUpPanel(
|
||||||
renderPanelSheet: false,
|
|
||||||
panel: _floatingPanel(),
|
panel: _floatingPanel(),
|
||||||
collapsed: _floatingCollapsed(),
|
// collapsed: _floatingCollapsed(),
|
||||||
body: MapWidget(trip: widget.trip)
|
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(){
|
Widget _floatingCollapsed(){
|
||||||
final ThemeData theme = Theme.of(context);
|
return Greeter(
|
||||||
return Container(
|
trip: widget.trip
|
||||||
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)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _floatingPanel(){
|
Widget _floatingPanel(){
|
||||||
final ThemeData theme = Theme.of(context);
|
return Column(
|
||||||
return Container(
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(15),
|
||||||
|
child:
|
||||||
|
Center(
|
||||||
|
child: Container(
|
||||||
|
width: 40,
|
||||||
|
height: 5,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.grey[300],
|
||||||
borderRadius: BorderRadius.all(Radius.circular(24.0)),
|
borderRadius: BorderRadius.all(Radius.circular(12.0)),
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
blurRadius: 20.0,
|
|
||||||
color: theme.shadowColor,
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Greeter(trip: widget.trip),
|
||||||
|
LandmarksOverview(trip: widget.trip)
|
||||||
]
|
]
|
||||||
),
|
)
|
||||||
child: Center(
|
)
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(8.0),
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
children: <Widget>[
|
|
||||||
Greeter(standalone: false, trip: widget.trip),
|
|
||||||
LandmarksOverview(trip: widget.trip),
|
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,35 +5,56 @@ import 'dart:collection';
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:anyway/structs/landmark.dart';
|
import 'package:anyway/structs/landmark.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:geocoding/geocoding.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class Trip {
|
class Trip with ChangeNotifier {
|
||||||
final String uuid;
|
String uuid;
|
||||||
final String cityName;
|
int totalTime;
|
||||||
// TODO: cityName should be inferred from coordinates of the Landmarks
|
LinkedList<Landmark> landmarks;
|
||||||
final int totalTime;
|
|
||||||
final LinkedList<Landmark> landmarks;
|
|
||||||
// could be empty as well
|
// could be empty as well
|
||||||
|
|
||||||
|
Future<String> get cityName async {
|
||||||
|
if (GeocodingPlatform.instance == null) {
|
||||||
|
return '${landmarks.first.location[0]}, ${landmarks.first.location[1]}';
|
||||||
|
}
|
||||||
|
List<Placemark> placemarks = await placemarkFromCoordinates(landmarks.first.location[0], landmarks.first.location[1]);
|
||||||
|
return placemarks.first.locality ?? 'Unknown';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Trip({
|
Trip({
|
||||||
required this.uuid,
|
this.uuid = 'pending',
|
||||||
required this.cityName,
|
this.totalTime = 0,
|
||||||
required this.landmarks,
|
LinkedList<Landmark>? landmarks
|
||||||
this.totalTime = 0
|
// a trip can be created with no landmarks, but the list should be initialized anyway
|
||||||
});
|
}) : landmarks = landmarks ?? LinkedList<Landmark>();
|
||||||
|
|
||||||
|
|
||||||
factory Trip.fromJson(Map<String, dynamic> json) {
|
factory Trip.fromJson(Map<String, dynamic> json) {
|
||||||
Trip trip = Trip(
|
Trip trip = Trip(
|
||||||
uuid: json['uuid'],
|
uuid: json['uuid'],
|
||||||
cityName: json['city_name'] ?? 'Not communicated',
|
totalTime: json['total_time'],
|
||||||
landmarks: LinkedList()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return trip;
|
return trip;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadFromJson(Map<String, dynamic> 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) {
|
factory Trip.fromPrefs(SharedPreferences prefs, String uuid) {
|
||||||
String? content = prefs.getString('trip_$uuid');
|
String? content = prefs.getString('trip_$uuid');
|
||||||
@ -47,7 +68,7 @@ class Trip {
|
|||||||
|
|
||||||
Map<String, dynamic> toJson() => {
|
Map<String, dynamic> toJson() => {
|
||||||
'uuid': uuid,
|
'uuid': uuid,
|
||||||
'city_name': cityName,
|
'total_time': totalTime,
|
||||||
'first_landmark_uuid': landmarks.first.uuid
|
'first_landmark_uuid': landmarks.first.uuid
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "dart:convert";
|
import "dart:convert";
|
||||||
import "dart:developer";
|
import "dart:developer";
|
||||||
|
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
import 'package:anyway/constants.dart';
|
import 'package:anyway/constants.dart';
|
||||||
import "package:anyway/structs/landmark.dart";
|
import "package:anyway/structs/landmark.dart";
|
||||||
import "package:anyway/structs/trip.dart";
|
import "package:anyway/structs/trip.dart";
|
||||||
@ -25,14 +25,14 @@ Dio dio = Dio(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<Trip>? fetchTrip(
|
fetchTrip(
|
||||||
List<double> startPoint,
|
Trip trip,
|
||||||
Future<UserPreferences> preferences,
|
Future<UserPreferences> preferences,
|
||||||
) async {
|
) async {
|
||||||
UserPreferences prefs = await preferences;
|
UserPreferences prefs = await preferences;
|
||||||
Map<String, dynamic> data = {
|
Map<String, dynamic> data = {
|
||||||
"preferences": prefs.toJson(),
|
"preferences": prefs.toJson(),
|
||||||
"start": startPoint
|
"start": trip.landmarks!.first.location,
|
||||||
};
|
};
|
||||||
String dataString = jsonEncode(data);
|
String dataString = jsonEncode(data);
|
||||||
log(dataString);
|
log(dataString);
|
||||||
@ -44,24 +44,25 @@ Future<Trip>? fetchTrip(
|
|||||||
|
|
||||||
// handle errors
|
// handle errors
|
||||||
if (response.statusCode != 200) {
|
if (response.statusCode != 200) {
|
||||||
|
trip.updateUUID("error");
|
||||||
throw Exception('Failed to load trip');
|
throw Exception('Failed to load trip');
|
||||||
}
|
}
|
||||||
if (response.data["error"] != null) {
|
if (response.data["error"] != null) {
|
||||||
|
trip.updateUUID("error");
|
||||||
throw Exception(response.data["error"]);
|
throw Exception(response.data["error"]);
|
||||||
}
|
}
|
||||||
log(response.data.toString());
|
log(response.data.toString());
|
||||||
Map<String, dynamic> json = response.data;
|
Map<String, dynamic> json = response.data;
|
||||||
|
|
||||||
// only fetch the trip "meta" data for now
|
// only fill in the trip "meta" data for now
|
||||||
Trip trip = Trip.fromJson(json);
|
trip.loadFromJson(json);
|
||||||
|
|
||||||
String? nextUUID = json["first_landmark_uuid"];
|
String? nextUUID = json["first_landmark_uuid"];
|
||||||
while (nextUUID != null) {
|
while (nextUUID != null) {
|
||||||
var (landmark, newUUID) = await fetchLandmark(nextUUID);
|
var (landmark, newUUID) = await fetchLandmark(nextUUID);
|
||||||
trip.landmarks.add(landmark);
|
trip.addLandmark(landmark);
|
||||||
nextUUID = newUUID;
|
nextUUID = newUUID;
|
||||||
}
|
}
|
||||||
return trip;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ Future<List<Trip>> loadTrips() async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (trips.isEmpty) {
|
if (trips.isEmpty) {
|
||||||
Trip t1 = Trip(uuid: '1', cityName: 'Paris', landmarks: LinkedList<Landmark>());
|
Trip t1 = Trip(uuid: '1', landmarks: LinkedList<Landmark>());
|
||||||
t1.landmarks.add(
|
t1.landmarks.add(
|
||||||
Landmark(
|
Landmark(
|
||||||
uuid: '1',
|
uuid: '1',
|
||||||
@ -66,7 +66,7 @@ Future<List<Trip>> loadTrips() async {
|
|||||||
trips.add(t1);
|
trips.add(t1);
|
||||||
|
|
||||||
|
|
||||||
Trip t2 = Trip(uuid: '2', cityName: 'Vienna', landmarks: LinkedList<Landmark>());
|
Trip t2 = Trip(uuid: '2', landmarks: LinkedList<Landmark>());
|
||||||
|
|
||||||
t2.landmarks.add(
|
t2.landmarks.add(
|
||||||
Landmark(
|
Landmark(
|
||||||
|
@ -25,6 +25,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
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:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -168,6 +192,38 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
google_maps:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -296,6 +352,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.12.0"
|
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:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -41,6 +41,8 @@ dependencies:
|
|||||||
dio: ^5.5.0+1
|
dio: ^5.5.0+1
|
||||||
google_maps_flutter: ^2.7.0
|
google_maps_flutter: ^2.7.0
|
||||||
the_widget_marker: ^1.0.0
|
the_widget_marker: ^1.0.0
|
||||||
|
cached_network_image: ^3.4.0
|
||||||
|
geocoding: ^3.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user