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