reworked page layout inheritence
This commit is contained in:
parent
8f6dfd404d
commit
56c55883ea
@ -1,72 +1,51 @@
|
||||
import 'package:anyway/main.dart';
|
||||
import 'package:anyway/modules/help_dialog.dart';
|
||||
import 'package:anyway/pages/current_trip.dart';
|
||||
import 'package:anyway/pages/settings.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:anyway/main.dart';
|
||||
import 'package:anyway/modules/help_dialog.dart';
|
||||
import 'package:anyway/modules/trips_saved_list.dart';
|
||||
import 'package:anyway/utils/load_trips.dart';
|
||||
|
||||
import 'package:anyway/pages/new_trip_location.dart';
|
||||
import 'package:anyway/pages/onboarding.dart';
|
||||
import 'package:anyway/pages/current_trip.dart';
|
||||
import 'package:anyway/pages/settings.dart';
|
||||
import 'package:anyway/pages/new_trip_location.dart';
|
||||
|
||||
|
||||
|
||||
|
||||
// BasePage is the scaffold that holds a child page and a side drawer
|
||||
// The side drawer is the main way to switch between pages
|
||||
|
||||
class BasePage extends StatefulWidget {
|
||||
final Widget mainScreen;
|
||||
final Widget title;
|
||||
final List<String> helpTexts;
|
||||
|
||||
const BasePage({
|
||||
super.key,
|
||||
required this.mainScreen,
|
||||
this.title = const Text(APP_NAME),
|
||||
this.helpTexts = const [],
|
||||
});
|
||||
|
||||
@override
|
||||
State<BasePage> createState() => _BasePageState();
|
||||
}
|
||||
|
||||
class _BasePageState extends State<BasePage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
savedTrips.loadTrips();
|
||||
|
||||
|
||||
mixin ScaffoldLayout<T extends StatefulWidget> on State<T> {
|
||||
Widget mainScaffold(
|
||||
BuildContext context,
|
||||
{
|
||||
Widget child = const Text("emptiness"),
|
||||
Widget title = const Text(APP_NAME),
|
||||
List<String> helpTexts = const []
|
||||
}
|
||||
) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: widget.title,
|
||||
title: title,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.help),
|
||||
tooltip: 'Help',
|
||||
onPressed: () {
|
||||
if (widget.helpTexts.isNotEmpty) {
|
||||
helpDialog(context, widget.helpTexts[0], widget.helpTexts[1]);
|
||||
if (helpTexts.isNotEmpty) {
|
||||
helpDialog(context, helpTexts[0], helpTexts[1]);
|
||||
}
|
||||
}
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Center(child: widget.mainScreen),
|
||||
body: Center(child: child),
|
||||
drawer: Drawer(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
decoration: const BoxDecoration(
|
||||
gradient: APP_GRADIENT,
|
||||
),
|
||||
height: 150,
|
||||
child: Center(
|
||||
child: const Center(
|
||||
child: Text(
|
||||
APP_NAME,
|
||||
style: TextStyle(
|
||||
@ -81,8 +60,7 @@ class _BasePageState extends State<BasePage> {
|
||||
ListTile(
|
||||
title: const Text('Your Trips'),
|
||||
leading: const Icon(Icons.map),
|
||||
// TODO: this is not working!
|
||||
selected: widget.mainScreen is TripPage,
|
||||
selected: widget is TripPage,
|
||||
onTap: () {},
|
||||
trailing: ElevatedButton(
|
||||
onPressed: () {
|
||||
@ -111,13 +89,12 @@ class _BasePageState extends State<BasePage> {
|
||||
const Divider(indent: 10, endIndent: 10),
|
||||
ListTile(
|
||||
title: const Text('How to use'),
|
||||
leading: Icon(Icons.help),
|
||||
// TODO: this is not working!
|
||||
selected: widget.mainScreen is OnboardingPage,
|
||||
leading: const Icon(Icons.help),
|
||||
selected: widget is OnboardingPage,
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => OnboardingPage()
|
||||
builder: (context) => const OnboardingPage()
|
||||
)
|
||||
);
|
||||
},
|
||||
@ -127,8 +104,7 @@ class _BasePageState extends State<BasePage> {
|
||||
ListTile(
|
||||
title: const Text('Settings'),
|
||||
leading: const Icon(Icons.settings),
|
||||
// TODO: this is not working!
|
||||
selected: widget.mainScreen is SettingsPage,
|
||||
selected: widget is SettingsPage,
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
@ -1,24 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
import 'package:anyway/utils/get_first_page.dart';
|
||||
import 'package:anyway/utils/load_trips.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:anyway/constants.dart';
|
||||
|
||||
|
||||
void main() => runApp(const App());
|
||||
|
||||
// Some global variables
|
||||
final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||
final SavedTrips savedTrips = SavedTrips();
|
||||
// the list of saved trips is then populated implicitly by getFirstPage()
|
||||
|
||||
|
||||
class App extends StatelessWidget {
|
||||
const App({super.key});
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: APP_NAME,
|
||||
home: getFirstPage(),
|
||||
theme: APP_THEME,
|
||||
scaffoldMessengerKey: rootScaffoldMessengerKey
|
||||
);
|
||||
}
|
||||
Widget build(BuildContext context) => MaterialApp(
|
||||
title: APP_NAME,
|
||||
home: getFirstPage(),
|
||||
theme: APP_THEME,
|
||||
scaffoldMessengerKey: rootScaffoldMessengerKey
|
||||
);
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import 'dart:developer';
|
||||
import 'package:anyway/modules/step_between_landmarks.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:anyway/modules/landmark_card.dart';
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:anyway/modules/step_between_landmarks.dart';
|
||||
import 'package:anyway/modules/landmark_card.dart';
|
||||
|
||||
|
||||
// Returns a list of widgets that represent the landmarks matching the given selector
|
||||
@ -35,4 +34,3 @@ List<Widget> landmarksList(Trip trip, {required bool Function(Landmark) selector
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
|
@ -35,29 +35,29 @@ class _CurrentTripLoadingIndicatorState extends State<CurrentTripLoadingIndicato
|
||||
// In the very center of the panel, show the greeter which tells the user that the trip is being generated
|
||||
Center(child: loadingText(widget.trip)),
|
||||
// As a gimmick, and a way to show that the app is still working, show a few loading dots
|
||||
Align(
|
||||
const Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: statusText(),
|
||||
child: StatusText(),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// automatically cycle through the greeter texts
|
||||
class statusText extends StatefulWidget {
|
||||
const statusText({Key? key}) : super(key: key);
|
||||
class StatusText extends StatefulWidget {
|
||||
const StatusText({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_statusTextState createState() => _statusTextState();
|
||||
_StatusTextState createState() => _StatusTextState();
|
||||
}
|
||||
|
||||
class _statusTextState extends State<statusText> {
|
||||
class _StatusTextState extends State<StatusText> {
|
||||
int statusIndex = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Future.delayed(Duration(seconds: 5), () {
|
||||
Future.delayed(const Duration(seconds: 5), () {
|
||||
setState(() {
|
||||
statusIndex = (statusIndex + 1) % statusTexts.length;
|
||||
});
|
||||
@ -159,4 +159,3 @@ class _AnimatedGradientTextState extends State<AnimatedGradientText> with Single
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
import 'dart:collection';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:widget_to_marker/widget_to_marker.dart';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
import 'package:anyway/modules/landmark_map_marker.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:widget_to_marker/widget_to_marker.dart';
|
||||
import 'package:anyway/modules/landmark_map_marker.dart';
|
||||
|
||||
|
||||
class CurrentTripMap extends StatefulWidget {
|
||||
final Trip? trip;
|
||||
@ -60,25 +61,29 @@ class _CurrentTripMapState extends State<CurrentTripMap> {
|
||||
}
|
||||
|
||||
void setMapMarkers() async {
|
||||
List<Landmark> landmarks = widget.trip?.landmarks.toList() ?? [];
|
||||
Set<Marker> markers = <Marker>{};
|
||||
Iterator<(int, Landmark)> it = (widget.trip?.landmarks.toList() ?? []).indexed.iterator;
|
||||
|
||||
for (int i = 0; i < landmarks.length; i++) {
|
||||
Landmark landmark = landmarks[i];
|
||||
while (it.moveNext()) {
|
||||
int i = it.current.$1;
|
||||
Landmark landmark = it.current.$2;
|
||||
|
||||
MarkerId markerId = MarkerId("${landmark.uuid} - ${landmark.visited}");
|
||||
List<double> location = landmark.location;
|
||||
Marker marker = Marker(
|
||||
markerId: MarkerId(landmark.uuid),
|
||||
position: LatLng(location[0], location[1]),
|
||||
icon: await ThemedMarker(landmark: landmark, position: i).toBitmapDescriptor(
|
||||
logicalSize: const Size(150, 150),
|
||||
imageSize: const Size(150, 150),
|
||||
),
|
||||
);
|
||||
markers.add(marker);
|
||||
// only create a new marker, if there is no marker for this landmark
|
||||
if (!mapMarkers.any((Marker marker) => marker.markerId == markerId)) {
|
||||
Marker marker = Marker(
|
||||
markerId: markerId,
|
||||
position: LatLng(location[0], location[1]),
|
||||
icon: await ThemedMarker(landmark: landmark, position: i).toBitmapDescriptor(
|
||||
logicalSize: const Size(150, 150),
|
||||
imageSize: const Size(150, 150),
|
||||
)
|
||||
);
|
||||
setState(() {
|
||||
mapMarkers.add(marker);
|
||||
});
|
||||
}
|
||||
}
|
||||
setState(() {
|
||||
mapMarkers = markers;
|
||||
});
|
||||
}
|
||||
|
||||
void setMapRoute() async {
|
||||
@ -98,8 +103,8 @@ class _CurrentTripMapState extends State<CurrentTripMap> {
|
||||
Polyline stepLine = Polyline(
|
||||
polylineId: PolylineId('step-${landmark.uuid}'),
|
||||
points: step,
|
||||
color: landmark.visited ? Colors.grey : PRIMARY_COLOR,
|
||||
width: 5,
|
||||
color: landmark.visited || (landmark.next?.visited ?? false) ? Colors.grey : PRIMARY_COLOR,
|
||||
width: 5
|
||||
);
|
||||
polyLines.add(stepLine);
|
||||
}
|
||||
|
@ -1,10 +1,12 @@
|
||||
import 'package:anyway/constants.dart';
|
||||
import 'package:anyway/modules/current_trip_error_message.dart';
|
||||
import 'package:anyway/modules/current_trip_loading_indicator.dart';
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
|
||||
import 'package:anyway/modules/current_trip_error_message.dart';
|
||||
import 'package:anyway/modules/current_trip_loading_indicator.dart';
|
||||
import 'package:anyway/modules/current_trip_summary.dart';
|
||||
import 'package:anyway/modules/current_trip_save_button.dart';
|
||||
import 'package:anyway/modules/current_trip_landmarks_list.dart';
|
||||
@ -74,20 +76,21 @@ class _CurrentTripPanelState extends State<CurrentTripPanel> {
|
||||
child: Column(
|
||||
children: [
|
||||
CurrentTripSummary(trip: widget.trip),
|
||||
ExpansionTile(
|
||||
leading: Icon(Icons.location_on),
|
||||
title: Text('Visited Landmarks (tap to expand)'),
|
||||
children: [
|
||||
...landmarksList(widget.trip, selector: (Landmark landmark) => landmark.visited),
|
||||
],
|
||||
visualDensity: VisualDensity.compact,
|
||||
collapsedShape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
if (widget.trip.landmarks.where((Landmark landmark) => landmark.visited).isNotEmpty)
|
||||
ExpansionTile(
|
||||
leading: const Icon(Icons.location_on),
|
||||
title: const Text('Visited Landmarks (tap to expand)'),
|
||||
children: [
|
||||
...landmarksList(widget.trip, selector: (Landmark landmark) => landmark.visited),
|
||||
],
|
||||
visualDensity: VisualDensity.compact,
|
||||
collapsedShape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -108,4 +111,4 @@ class _CurrentTripPanelState extends State<CurrentTripPanel> {
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
|
||||
import 'package:anyway/main.dart';
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:auto_size_text/auto_size_text.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
class saveButton extends StatefulWidget {
|
||||
@ -52,4 +52,3 @@ class _saveButtonState extends State<saveButton> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
class CurrentTripSummary extends StatefulWidget {
|
||||
final Trip trip;
|
||||
const CurrentTripSummary({
|
||||
@ -16,22 +17,22 @@ class _CurrentTripSummaryState extends State<CurrentTripSummary> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 20),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.flag, size: 20),
|
||||
Padding(padding: EdgeInsets.only(right: 10)),
|
||||
Text('Stops: ${widget.trip.landmarks.length}', style: Theme.of(context).textTheme.bodyLarge,),
|
||||
const Icon(Icons.flag, size: 20),
|
||||
const Padding(padding: EdgeInsets.only(right: 10)),
|
||||
Text('Stops: ${widget.trip.landmarks.length}', style: Theme.of(context).textTheme.bodyLarge),
|
||||
]
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.hourglass_bottom_rounded, size: 20),
|
||||
Padding(padding: EdgeInsets.only(right: 10)),
|
||||
Text('Duration: ${widget.trip.totalTime} minutes', style: Theme.of(context).textTheme.bodyLarge,),
|
||||
const Icon(Icons.hourglass_bottom_rounded, size: 20),
|
||||
const Padding(padding: EdgeInsets.only(right: 10)),
|
||||
Text('Duration: ${widget.trip.totalTime} minutes', style: Theme.of(context).textTheme.bodyLarge),
|
||||
]
|
||||
),
|
||||
],
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
||||
Future<void> helpDialog(BuildContext context, String title, String content) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
|
@ -1,12 +1,15 @@
|
||||
import 'package:anyway/constants.dart';
|
||||
import 'package:anyway/main.dart';
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
|
||||
import 'package:anyway/main.dart';
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
|
||||
|
||||
|
||||
class LandmarkCard extends StatefulWidget {
|
||||
final Landmark landmark;
|
||||
final Trip parentTrip;
|
||||
@ -23,20 +26,11 @@ class LandmarkCard extends StatefulWidget {
|
||||
|
||||
class _LandmarkCardState extends State<LandmarkCard> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.landmark.type == typeStart || widget.landmark.type == typeFinish) {
|
||||
return TextButton.icon(
|
||||
onPressed: () {},
|
||||
icon: widget.landmark.type.icon,
|
||||
label: Text(widget.landmark.name),
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
constraints: BoxConstraints(
|
||||
minHeight: 50,
|
||||
maxHeight: 200,
|
||||
// express the max height in terms text lines
|
||||
maxHeight: 7 * (Theme.of(context).textTheme.titleMedium!.fontSize! + 10),
|
||||
),
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
@ -79,23 +73,23 @@ class _LandmarkCardState extends State<LandmarkCard> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Container(
|
||||
color: PRIMARY_COLOR,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 5,
|
||||
children: [
|
||||
Icon(widget.landmark.type.icon.icon, size: 16),
|
||||
Text(widget.landmark.type.name, style: TextStyle(fontWeight: FontWeight.bold)),
|
||||
],
|
||||
if (widget.landmark.type != typeStart && widget.landmark.type != typeFinish)
|
||||
Container(
|
||||
color: PRIMARY_COLOR,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(5),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
spacing: 5,
|
||||
children: [
|
||||
Icon(Icons.timer_outlined, size: 16),
|
||||
Text("${widget.landmark.duration?.inMinutes} minutes"),
|
||||
],
|
||||
)
|
||||
)
|
||||
)
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
),
|
||||
@ -133,12 +127,6 @@ class _LandmarkCardState extends State<LandmarkCard> {
|
||||
// show the type, the website, and the wikipedia link as buttons/labels in a row
|
||||
children: [
|
||||
doneToggleButton(),
|
||||
// if (widget.landmark.duration != null && widget.landmark.duration!.inMinutes > 0)
|
||||
// TextButton.icon(
|
||||
// onPressed: () {},
|
||||
// icon: Icon(Icons.hourglass_bottom),
|
||||
// label: Text('${widget.landmark.duration!.inMinutes} minutes'),
|
||||
// ),
|
||||
if (widget.landmark.websiteURL != null)
|
||||
websiteButton(),
|
||||
|
||||
@ -172,33 +160,35 @@ class _LandmarkCardState extends State<LandmarkCard> {
|
||||
// open a browser with the website link
|
||||
await launchUrl(Uri.parse(widget.landmark.websiteURL!));
|
||||
},
|
||||
icon: Icon(Icons.link),
|
||||
label: Text('Website'),
|
||||
icon: const Icon(Icons.link),
|
||||
label: const Text('Website'),
|
||||
);
|
||||
|
||||
|
||||
Widget optionsButton () => PopupMenuButton(
|
||||
icon: Icon(Icons.settings),
|
||||
icon: const Icon(Icons.settings),
|
||||
style: TextButtonTheme.of(context).style,
|
||||
itemBuilder: (context) => [
|
||||
PopupMenuItem(
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.delete),
|
||||
title: Text('Delete'),
|
||||
leading: const Icon(Icons.delete),
|
||||
title: const Text('Delete'),
|
||||
onTap: () async {
|
||||
widget.parentTrip.removeLandmark(widget.landmark);
|
||||
rootScaffoldMessengerKey.currentState!.showSnackBar(
|
||||
SnackBar(content: Text("We won't show ${widget.landmark.name} again"))
|
||||
SnackBar(content: Text("${widget.landmark.name} won't be shown again"))
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.star),
|
||||
title: Text('Favorite'),
|
||||
leading: const Icon(Icons.star),
|
||||
title: const Text('Favorite'),
|
||||
onTap: () async {
|
||||
// delete the landmark
|
||||
// await deleteLandmark(widget.landmark);
|
||||
rootScaffoldMessengerKey.currentState!.showSnackBar(
|
||||
SnackBar(content: Text("Not implemented yet"))
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
@ -46,11 +46,11 @@ class _NewTripButtonState extends State<NewTripButton> {
|
||||
UserPreferences preferences = widget.preferences;
|
||||
if (preferences.nature.value == 0 && preferences.shopping.value == 0 && preferences.sightseeing.value == 0){
|
||||
rootScaffoldMessengerKey.currentState!.showSnackBar(
|
||||
SnackBar(content: Text("Please specify at least one preference"))
|
||||
const SnackBar(content: Text("Please specify at least one preference"))
|
||||
);
|
||||
} else if (preferences.maxTime.value == 0){
|
||||
rootScaffoldMessengerKey.currentState!.showSnackBar(
|
||||
SnackBar(content: Text("Please choose a longer duration"))
|
||||
const SnackBar(content: Text("Please choose a longer duration"))
|
||||
);
|
||||
} else {
|
||||
Trip trip = widget.trip;
|
||||
@ -63,4 +63,3 @@ class _NewTripButtonState extends State<NewTripButton> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
// A map that allows the user to select a location for a new trip.
|
||||
import 'dart:developer';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:widget_to_marker/widget_to_marker.dart';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
import 'package:anyway/modules/landmark_map_marker.dart';
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:widget_to_marker/widget_to_marker.dart';
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
import 'package:anyway/modules/landmark_map_marker.dart';
|
||||
|
||||
|
||||
class NewTripMap extends StatefulWidget {
|
||||
|
@ -1,7 +1,9 @@
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:anyway/structs/landmark.dart';
|
||||
import 'package:anyway/modules/map_chooser.dart';
|
||||
|
||||
|
||||
class StepBetweenLandmarks extends StatefulWidget {
|
||||
final Landmark current;
|
||||
final Landmark next;
|
||||
@ -19,11 +21,15 @@ class StepBetweenLandmarks extends StatefulWidget {
|
||||
class _StepBetweenLandmarksState extends State<StepBetweenLandmarks> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
int time = widget.current.tripTime?.inMinutes ?? 0;
|
||||
int? time = widget.current.tripTime?.inMinutes;
|
||||
if (time != null && time < 1) {
|
||||
time = 1;
|
||||
}
|
||||
|
||||
return Container(
|
||||
margin: EdgeInsets.all(10),
|
||||
padding: EdgeInsets.all(10),
|
||||
decoration: BoxDecoration(
|
||||
margin: const EdgeInsets.all(10),
|
||||
padding: const EdgeInsets.all(10),
|
||||
decoration: const BoxDecoration(
|
||||
border: Border(
|
||||
left: BorderSide(width: 3.0, color: Colors.black),
|
||||
),
|
||||
@ -32,21 +38,22 @@ class _StepBetweenLandmarksState extends State<StepBetweenLandmarks> {
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Icon(Icons.directions_walk),
|
||||
Text("$time min", style: TextStyle(fontSize: 10)),
|
||||
const Icon(Icons.directions_walk),
|
||||
Text(
|
||||
time == null ? "" : "About $time min",
|
||||
style: const TextStyle(fontSize: 10)
|
||||
),
|
||||
],
|
||||
),
|
||||
Spacer(),
|
||||
ElevatedButton(
|
||||
|
||||
const Spacer(),
|
||||
|
||||
ElevatedButton.icon(
|
||||
onPressed: () async {
|
||||
showMapChooser(context, widget.current, widget.next);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.directions),
|
||||
Text("Directions"),
|
||||
],
|
||||
),
|
||||
icon: const Icon(Icons.directions),
|
||||
label: const Text("Directions"),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:anyway/constants.dart';
|
||||
import 'package:anyway/pages/base_page.dart';
|
||||
import 'package:anyway/layouts/scaffold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sliding_up_panel/sliding_up_panel.dart';
|
||||
|
||||
@ -28,12 +28,13 @@ class TripPage extends StatefulWidget {
|
||||
|
||||
|
||||
|
||||
class _TripPageState extends State<TripPage> {
|
||||
class _TripPageState extends State<TripPage> with ScaffoldLayout{
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
mainScreen: SlidingUpPanel(
|
||||
return mainScaffold(
|
||||
context,
|
||||
child: SlidingUpPanel(
|
||||
// use panelBuilder instead of panel so that we can reuse the scrollcontroller for the listview
|
||||
panelBuilder: (scrollcontroller) => CurrentTripPanel(controller: scrollcontroller, trip: widget.trip),
|
||||
// using collapsed and panelBuilder seems to show both at the same time, so we include the greeter in the panelBuilder
|
||||
@ -58,9 +59,13 @@ class _TripPageState extends State<TripPage> {
|
||||
title: FutureBuilder(
|
||||
future: widget.trip.cityName,
|
||||
builder: (context, snapshot) => Text(
|
||||
'Your trip to ${snapshot.hasData ? snapshot.data! : "..."}',
|
||||
'Trip to ${snapshot.hasData ? snapshot.data! : "..."}',
|
||||
)
|
||||
),
|
||||
helpTexts: [
|
||||
'Current trip',
|
||||
'You can see and edit your current trip here. Swipe up from the bottom to see a detailed view of the recommendations.'
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:anyway/layouts/scaffold.dart';
|
||||
import 'package:anyway/modules/new_trip_options_button.dart';
|
||||
import 'package:anyway/pages/base_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import "package:anyway/structs/trip.dart";
|
||||
@ -14,7 +14,7 @@ class NewTripPage extends StatefulWidget {
|
||||
_NewTripPageState createState() => _NewTripPageState();
|
||||
}
|
||||
|
||||
class _NewTripPageState extends State<NewTripPage> {
|
||||
class _NewTripPageState extends State<NewTripPage> with ScaffoldLayout {
|
||||
final TextEditingController latController = TextEditingController();
|
||||
final TextEditingController lonController = TextEditingController();
|
||||
Trip trip = Trip();
|
||||
@ -23,8 +23,9 @@ class _NewTripPageState extends State<NewTripPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// floating search bar and map as a background
|
||||
return BasePage(
|
||||
mainScreen: Scaffold(
|
||||
return mainScaffold(
|
||||
context,
|
||||
child: Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
NewTripMap(trip),
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:anyway/layouts/scaffold.dart';
|
||||
import 'package:anyway/modules/new_trip_button.dart';
|
||||
import 'package:anyway/pages/base_page.dart';
|
||||
import 'package:anyway/structs/preferences.dart';
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
@ -15,13 +15,14 @@ class NewTripPreferencesPage extends StatefulWidget {
|
||||
_NewTripPreferencesPageState createState() => _NewTripPreferencesPageState();
|
||||
}
|
||||
|
||||
class _NewTripPreferencesPageState extends State<NewTripPreferencesPage> {
|
||||
class _NewTripPreferencesPageState extends State<NewTripPreferencesPage> with ScaffoldLayout {
|
||||
UserPreferences preferences = UserPreferences();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
mainScreen: Scaffold(
|
||||
return mainScaffold(
|
||||
context,
|
||||
child: Scaffold(
|
||||
body: ListView(
|
||||
children: [
|
||||
// Center(
|
||||
@ -41,23 +42,22 @@ class _NewTripPreferencesPageState extends State<NewTripPreferencesPage> {
|
||||
// )
|
||||
// ),
|
||||
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 10, right: 10, top: 20, bottom: 0),
|
||||
child: Text('Tell us about your ideal trip.', style: TextStyle(fontSize: 18))
|
||||
),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 10, right: 10, top: 20, bottom: 0),
|
||||
child: Text('Tell us about your ideal trip.', style: TextStyle(fontSize: 18))
|
||||
),
|
||||
),
|
||||
|
||||
Divider(indent: 25, endIndent: 25, height: 50),
|
||||
Divider(indent: 25, endIndent: 25, height: 50),
|
||||
|
||||
durationPicker(preferences.maxTime),
|
||||
durationPicker(preferences.maxTime),
|
||||
|
||||
preferenceSliders([preferences.sightseeing, preferences.shopping, preferences.nature]),
|
||||
]
|
||||
),
|
||||
floatingActionButton: NewTripButton(trip: widget.trip, preferences: preferences),
|
||||
preferenceSliders([preferences.sightseeing, preferences.shopping, preferences.nature]),
|
||||
]
|
||||
),
|
||||
floatingActionButton: NewTripButton(trip: widget.trip, preferences: preferences),
|
||||
),
|
||||
|
||||
title: FutureBuilder(
|
||||
future: widget.trip.cityName,
|
||||
builder: (context, snapshot) => Text(
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:anyway/constants.dart';
|
||||
import 'package:anyway/layouts/scaffold.dart';
|
||||
import 'package:anyway/main.dart';
|
||||
import 'package:anyway/pages/base_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@ -14,42 +14,41 @@ class SettingsPage extends StatefulWidget {
|
||||
_SettingsPageState createState() => _SettingsPageState();
|
||||
}
|
||||
|
||||
class _SettingsPageState extends State<SettingsPage> {
|
||||
class _SettingsPageState extends State<SettingsPage> with ScaffoldLayout {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BasePage(
|
||||
mainScreen: ListView(
|
||||
padding: EdgeInsets.all(15),
|
||||
children: [
|
||||
// First a round, centered image
|
||||
Center(
|
||||
child: CircleAvatar(
|
||||
radius: 75,
|
||||
child: Icon(Icons.settings, size: 100),
|
||||
)
|
||||
),
|
||||
Center(
|
||||
child: Text('Global settings', style: TextStyle(fontSize: 24))
|
||||
),
|
||||
Widget build (BuildContext context) => mainScaffold(
|
||||
context,
|
||||
child: ListView(
|
||||
padding: EdgeInsets.all(15),
|
||||
children: [
|
||||
// First a round, centered image
|
||||
Center(
|
||||
child: CircleAvatar(
|
||||
radius: 75,
|
||||
child: Icon(Icons.settings, size: 100),
|
||||
)
|
||||
),
|
||||
Center(
|
||||
child: Text('Global settings', style: TextStyle(fontSize: 24))
|
||||
),
|
||||
|
||||
Divider(indent: 25, endIndent: 25, height: 50),
|
||||
Divider(indent: 25, endIndent: 25, height: 50),
|
||||
|
||||
darkMode(),
|
||||
setLocationUsage(),
|
||||
setDebugMode(),
|
||||
darkMode(),
|
||||
setLocationUsage(),
|
||||
setDebugMode(),
|
||||
|
||||
Divider(indent: 25, endIndent: 25, height: 50),
|
||||
Divider(indent: 25, endIndent: 25, height: 50),
|
||||
|
||||
privacyInfo(),
|
||||
]
|
||||
),
|
||||
title: Text('Settings'),
|
||||
helpTexts: [
|
||||
'Settings',
|
||||
'Preferences set in this page are global and will affect the entire application.'
|
||||
],
|
||||
);
|
||||
}
|
||||
privacyInfo(),
|
||||
]
|
||||
),
|
||||
title: Text('Settings'),
|
||||
helpTexts: [
|
||||
'Settings',
|
||||
'Preferences set in this page are global and will affect the entire application.'
|
||||
],
|
||||
);
|
||||
|
||||
Widget setDebugMode() {
|
||||
return Row(
|
||||
|
@ -70,10 +70,10 @@ final class Landmark extends LinkedListEntry<Landmark>{
|
||||
final websiteURL = json['website_url'] as String?;
|
||||
final imageURL = json['image_url'] as String?;
|
||||
final description = json['description'] as String?;
|
||||
var duration = Duration(minutes: json['duration'] ?? 0) as Duration?;
|
||||
final visited = json['visited'] ?? false as bool;
|
||||
var duration = Duration(minutes: json['duration']);
|
||||
final visited = json['visited'] ?? false;
|
||||
var tripTime = Duration(minutes: json['time_to_reach_next'] ?? 0) as Duration?;
|
||||
|
||||
|
||||
return Landmark(
|
||||
uuid: uuid,
|
||||
name: name,
|
||||
|
@ -29,6 +29,18 @@ class Trip with ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
Future<int> landmarkPosition (Landmark landmark) async {
|
||||
int i = 0;
|
||||
for (Landmark l in landmarks) {
|
||||
if (l.uuid == landmark.uuid) {
|
||||
return i;
|
||||
} else if (l.type != typeStart && l.type != typeFinish) {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
Trip({
|
||||
this.uuid = 'pending',
|
||||
|
@ -1,33 +1,33 @@
|
||||
import "dart:convert";
|
||||
import "dart:developer";
|
||||
import "package:anyway/utils/load_landmark_image.dart";
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
import 'package:anyway/constants.dart';
|
||||
import "package:anyway/utils/load_landmark_image.dart";
|
||||
import "package:anyway/structs/landmark.dart";
|
||||
import "package:anyway/structs/trip.dart";
|
||||
import "package:anyway/structs/preferences.dart";
|
||||
|
||||
|
||||
Dio dio = Dio(
|
||||
BaseOptions(
|
||||
baseUrl: API_URL_BASE,
|
||||
connectTimeout: const Duration(seconds: 5),
|
||||
receiveTimeout: const Duration(seconds: 120),
|
||||
// also accept 500 errors, since we cannot rule out that the server is at fault. We still want to gracefully handle these errors
|
||||
validateStatus: (status) => status! <= 500,
|
||||
receiveDataWhenStatusError: true,
|
||||
// api is notoriously slow
|
||||
// headers: {
|
||||
// HttpHeaders.userAgentHeader: 'dio',
|
||||
// 'api': '1.0.0',
|
||||
// },
|
||||
contentType: Headers.jsonContentType,
|
||||
responseType: ResponseType.json,
|
||||
|
||||
BaseOptions(
|
||||
baseUrl: API_URL_BASE,
|
||||
connectTimeout: const Duration(seconds: 5),
|
||||
receiveTimeout: const Duration(seconds: 120),
|
||||
// also accept 500 errors, since we cannot rule out that the server is at fault. We still want to gracefully handle these errors
|
||||
validateStatus: (status) => status! <= 500,
|
||||
receiveDataWhenStatusError: true,
|
||||
// api is notoriously slow
|
||||
// headers: {
|
||||
// HttpHeaders.userAgentHeader: 'dio',
|
||||
// 'api': '1.0.0',
|
||||
// },
|
||||
contentType: Headers.jsonContentType,
|
||||
responseType: ResponseType.json,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
fetchTrip(
|
||||
Trip trip,
|
||||
UserPreferences preferences,
|
||||
|
@ -1,11 +1,14 @@
|
||||
import 'package:anyway/pages/current_trip.dart';
|
||||
import 'package:anyway/pages/onboarding.dart';
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:anyway/utils/load_trips.dart';
|
||||
import 'package:anyway/main.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:anyway/structs/trip.dart';
|
||||
import 'package:anyway/utils/load_trips.dart';
|
||||
import 'package:anyway/pages/current_trip.dart';
|
||||
import 'package:anyway/pages/onboarding.dart';
|
||||
|
||||
|
||||
Widget getFirstPage() {
|
||||
SavedTrips trips = SavedTrips();
|
||||
SavedTrips trips = savedTrips;
|
||||
trips.loadTrips();
|
||||
|
||||
return ListenableBuilder(
|
||||
@ -15,7 +18,7 @@ Widget getFirstPage() {
|
||||
if (items.isNotEmpty) {
|
||||
return TripPage(trip: items[0]);
|
||||
} else {
|
||||
return OnboardingPage();
|
||||
return const OnboardingPage();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user