account for changed itineraries once landmarks are marked as done or deleted
This commit is contained in:
		| @@ -1,3 +1,5 @@ | |||||||
|  | import 'dart:developer'; | ||||||
|  |  | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
|  |  | ||||||
| import 'package:anyway/structs/landmark.dart'; | import 'package:anyway/structs/landmark.dart'; | ||||||
| @@ -24,13 +26,19 @@ List<Widget> landmarksList(Trip trip, {required bool Function(Landmark) selector | |||||||
|         LandmarkCard(landmark, trip), |         LandmarkCard(landmark, trip), | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
|       if (!landmark.visited && landmark.next != null) { |       if (!landmark.visited) { | ||||||
|  |         Landmark? nextLandmark = landmark.next; | ||||||
|  |         while (nextLandmark != null && nextLandmark.visited) { | ||||||
|  |           nextLandmark = nextLandmark.next; | ||||||
|  |         }  | ||||||
|  |         if (nextLandmark != null) { | ||||||
|           children.add( |           children.add( | ||||||
|           StepBetweenLandmarks(current: landmark, next: landmark.next!) |             StepBetweenLandmarks(current: landmark, next: nextLandmark!) | ||||||
|           ); |           ); | ||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   return children; |   return children; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import 'package:anyway/constants.dart'; | import 'dart:async'; | ||||||
|  |  | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:auto_size_text/auto_size_text.dart'; | import 'package:auto_size_text/auto_size_text.dart'; | ||||||
|  |  | ||||||
| @@ -37,8 +38,11 @@ class _CurrentTripLoadingIndicatorState extends State<CurrentTripLoadingIndicato | |||||||
|       // 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 | ||||||
|       const Align( |       const Align( | ||||||
|         alignment: Alignment.bottomCenter, |         alignment: Alignment.bottomCenter, | ||||||
|  |         child: Padding( | ||||||
|  |           padding: EdgeInsets.only(bottom: 12), | ||||||
|           child: StatusText(), |           child: StatusText(), | ||||||
|         ) |         ) | ||||||
|  |       ) | ||||||
|     ], |     ], | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
| @@ -81,19 +85,19 @@ Widget loadingText(Trip trip) => FutureBuilder( | |||||||
|     Widget greeter; |     Widget greeter; | ||||||
|  |  | ||||||
|     if (snapshot.hasData) { |     if (snapshot.hasData) { | ||||||
|       greeter = AnimatedGradientText( |       greeter = AnimatedDotsText( | ||||||
|         text: 'Creating your trip to ${snapshot.data}...', |         baseText: 'Creating your trip to ${snapshot.data}', | ||||||
|         style: greeterStyle, |         style: greeterStyle, | ||||||
|       ); |       ); | ||||||
|     } else if (snapshot.hasError) { |     } else if (snapshot.hasError) { | ||||||
|       // the exact error is shown in the central part of the trip overview. No need to show it here |       // the exact error is shown in the central part of the trip overview. No need to show it here | ||||||
|       greeter = AnimatedGradientText( |       greeter = Text( | ||||||
|         text: 'Error while loading trip.', |         'Error while loading trip.', | ||||||
|         style: greeterStyle, |         style: greeterStyle, | ||||||
|       ); |       ); | ||||||
|     } else { |     } else { | ||||||
|       greeter = AnimatedGradientText( |       greeter = AnimatedDotsText( | ||||||
|         text: 'Creating your trip...', |         baseText: 'Creating your trip', | ||||||
|         style: greeterStyle, |         style: greeterStyle, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| @@ -101,61 +105,44 @@ Widget loadingText(Trip trip) => FutureBuilder( | |||||||
|   } |   } | ||||||
| ); | ); | ||||||
|  |  | ||||||
| class AnimatedGradientText extends StatefulWidget { | class AnimatedDotsText extends StatefulWidget { | ||||||
|   final String text; |   final String baseText; | ||||||
|   final TextStyle style; |   final TextStyle style; | ||||||
|  |  | ||||||
|   const AnimatedGradientText({ |   const AnimatedDotsText({ | ||||||
|     Key? key, |     Key? key, | ||||||
|     required this.text, |     required this.baseText, | ||||||
|     required this.style, |     required this.style, | ||||||
|   }) : super(key: key); |   }) : super(key: key); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   _AnimatedGradientTextState createState() => _AnimatedGradientTextState(); |   _AnimatedDotsTextState createState() => _AnimatedDotsTextState(); | ||||||
| } | } | ||||||
|  |  | ||||||
| class _AnimatedGradientTextState extends State<AnimatedGradientText> with SingleTickerProviderStateMixin { | class _AnimatedDotsTextState extends State<AnimatedDotsText> { | ||||||
|   late AnimationController _controller; |   int dotCount = 0; | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   void initState() { |   void initState() { | ||||||
|     super.initState(); |     super.initState(); | ||||||
|     _controller = AnimationController( |     Timer.periodic(const Duration(seconds: 1), (timer) { | ||||||
|       duration: const Duration(seconds: 1), |       if (mounted) { | ||||||
|       vsync: this, |         setState(() { | ||||||
|     )..repeat(); |           dotCount = (dotCount + 1) % 4; | ||||||
|  |           // show up to 3 dots | ||||||
|  |         }); | ||||||
|  |       } else { | ||||||
|  |         timer.cancel(); | ||||||
|       } |       } | ||||||
|  |     }); | ||||||
|   @override |  | ||||||
|   void dispose() { |  | ||||||
|     _controller.dispose(); |  | ||||||
|     super.dispose(); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return AnimatedBuilder( |     String dots = '.' * dotCount; | ||||||
|       animation: _controller, |     return Text( | ||||||
|       builder: (context, child) { |       '${widget.baseText}$dots', | ||||||
|         return ShaderMask( |  | ||||||
|           shaderCallback: (bounds) { |  | ||||||
|             return LinearGradient( |  | ||||||
|               colors: [GRADIENT_START, GRADIENT_END, GRADIENT_START], |  | ||||||
|               stops: [ |  | ||||||
|                 _controller.value - 1.0, |  | ||||||
|                 _controller.value, |  | ||||||
|                 _controller.value + 1.0, |  | ||||||
|               ], |  | ||||||
|               tileMode: TileMode.mirror, |  | ||||||
|             ).createShader(bounds); |  | ||||||
|           }, |  | ||||||
|           child: Text( |  | ||||||
|             widget.text, |  | ||||||
|       style: widget.style, |       style: widget.style, | ||||||
|           ), |  | ||||||
|         ); |  | ||||||
|       }, |  | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -47,32 +47,20 @@ class _LandmarkCardState extends State<LandmarkCard> { | |||||||
|             AspectRatio( |             AspectRatio( | ||||||
|               aspectRatio: 3 / 4, |               aspectRatio: 3 / 4, | ||||||
|               child: Column( |               child: Column( | ||||||
|  |                 crossAxisAlignment: CrossAxisAlignment.stretch, | ||||||
|                 children: [ |                 children: [ | ||||||
|                   if (widget.landmark.imageURL != null && widget.landmark.imageURL!.isNotEmpty) |                   if (widget.landmark.imageURL != null && widget.landmark.imageURL!.isNotEmpty) | ||||||
|                     Expanded( |                     Expanded( | ||||||
|                       child: CachedNetworkImage( |                       child: CachedNetworkImage( | ||||||
|                         imageUrl: widget.landmark.imageURL!, |                         imageUrl: widget.landmark.imageURL!, | ||||||
|                         placeholder: (context, url) => Center(child: CircularProgressIndicator()), |                         placeholder: (context, url) => const Center(child: CircularProgressIndicator()), | ||||||
|                         errorWidget: (context, error, stackTrace) => Icon(Icons.question_mark_outlined), |                         errorWidget: (context, url, error) => imagePlaceholder(widget.landmark), | ||||||
|                         fit: BoxFit.cover, |                         fit: BoxFit.cover | ||||||
|                       ) |                       ) | ||||||
|                     ) |                     ) | ||||||
|                   else |                   else | ||||||
|                     Expanded( |                     imagePlaceholder(widget.landmark), | ||||||
|                       child:  |  | ||||||
|                       Container( |  | ||||||
|                         decoration: BoxDecoration( |  | ||||||
|                           gradient: LinearGradient( |  | ||||||
|                             begin: Alignment.topLeft, |  | ||||||
|                             end: Alignment.bottomRight, |  | ||||||
|                             colors: [GRADIENT_START, GRADIENT_END], |  | ||||||
|                           ), |  | ||||||
|                         ), |  | ||||||
|                         child: Center( |  | ||||||
|                           child: Icon(widget.landmark.type.icon.icon, size: 50), |  | ||||||
|                         ), |  | ||||||
|                       ), |  | ||||||
|                     ), |  | ||||||
|                   if (widget.landmark.type != typeStart && widget.landmark.type != typeFinish) |                   if (widget.landmark.type != typeStart && widget.landmark.type != typeFinish) | ||||||
|                     Container( |                     Container( | ||||||
|                       color: PRIMARY_COLOR, |                       color: PRIMARY_COLOR, | ||||||
| @@ -96,7 +84,10 @@ class _LandmarkCardState extends State<LandmarkCard> { | |||||||
|  |  | ||||||
|             // Main information, useful buttons on the right |             // Main information, useful buttons on the right | ||||||
|             Expanded( |             Expanded( | ||||||
|               child: Padding( |               child: Column( | ||||||
|  |                 crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                 children: [ | ||||||
|  |                   Padding( | ||||||
|                     padding: const EdgeInsets.all(10), |                     padding: const EdgeInsets.all(10), | ||||||
|                     child: Column( |                     child: Column( | ||||||
|                       crossAxisAlignment: CrossAxisAlignment.start, |                       crossAxisAlignment: CrossAxisAlignment.start, | ||||||
| @@ -115,13 +106,18 @@ class _LandmarkCardState extends State<LandmarkCard> { | |||||||
|                             maxLines: 1, |                             maxLines: 1, | ||||||
|                             overflow: TextOverflow.ellipsis, |                             overflow: TextOverflow.ellipsis, | ||||||
|                           ), |                           ), | ||||||
|  |                       ] | ||||||
|  |                     ), | ||||||
|  |                   ), | ||||||
|  |  | ||||||
|                   // fill the vspace |                   // fill the vspace | ||||||
|                   const Spacer(), |                   const Spacer(), | ||||||
|  |  | ||||||
|                   SingleChildScrollView( |                   SingleChildScrollView( | ||||||
|                       // allows the buttons to be scrolled |  | ||||||
|                     scrollDirection: Axis.horizontal, |                     scrollDirection: Axis.horizontal, | ||||||
|  |                     padding: EdgeInsets.only(left: 5, right: 5, bottom: 10), | ||||||
|  |                     // the scroll view should be flush once the buttons are scrolled to the left | ||||||
|  |                     // but initially there should be some padding | ||||||
|                     child: Wrap( |                     child: Wrap( | ||||||
|                       spacing: 10, |                       spacing: 10, | ||||||
|                       // 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 | ||||||
| @@ -135,8 +131,7 @@ class _LandmarkCardState extends State<LandmarkCard> { | |||||||
|                     ), |                     ), | ||||||
|                   ), |                   ), | ||||||
|                 ], |                 ], | ||||||
|                 ), |               ) | ||||||
|               ), |  | ||||||
|             ) |             ) | ||||||
|           ], |           ], | ||||||
|         ) |         ) | ||||||
| @@ -195,3 +190,21 @@ class _LandmarkCardState extends State<LandmarkCard> { | |||||||
|     ], |     ], | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Widget imagePlaceholder (Landmark landmark) => Expanded( | ||||||
|  |   child:  | ||||||
|  |   Container( | ||||||
|  |     decoration: const BoxDecoration( | ||||||
|  |       gradient: LinearGradient( | ||||||
|  |         begin: Alignment.topLeft, | ||||||
|  |         end: Alignment.bottomRight, | ||||||
|  |         colors: [GRADIENT_START, GRADIENT_END], | ||||||
|  |       ), | ||||||
|  |     ), | ||||||
|  |     child: Center( | ||||||
|  |       child: Icon(landmark.type.icon.icon, size: 50), | ||||||
|  |     ), | ||||||
|  |   ), | ||||||
|  | ); | ||||||
|   | |||||||
| @@ -22,6 +22,14 @@ class _StepBetweenLandmarksState extends State<StepBetweenLandmarks> { | |||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     int? time = widget.current.tripTime?.inMinutes; |     int? time = widget.current.tripTime?.inMinutes; | ||||||
|  |      | ||||||
|  |     // since landmarks might have been marked as visited, the next landmark might not be the immediate next in the list | ||||||
|  |     // => the precomputed trip time is not valid anymore | ||||||
|  |     if (widget.current.next != widget.next) { | ||||||
|  |       time = null; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // round 0 travel time to 1 minute | ||||||
|     if (time != null && time < 1) { |     if (time != null && time < 1) { | ||||||
|       time = 1; |       time = 1; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -29,9 +29,10 @@ final class Landmark extends LinkedListEntry<Landmark>{ | |||||||
|   final Duration? duration; |   final Duration? duration; | ||||||
|   bool visited; |   bool visited; | ||||||
|  |  | ||||||
|   // Next node |   // Next node is implicitly available through the LinkedListEntry mixin | ||||||
|   // final Landmark? next; |   // final Landmark? next; | ||||||
|   final Duration? tripTime; |   Duration? tripTime; | ||||||
|  |   // the trip time depends on the next landmark, so it is not final | ||||||
|  |  | ||||||
|  |  | ||||||
|   Landmark({ |   Landmark({ | ||||||
|   | |||||||
| @@ -75,8 +75,16 @@ class Trip with ChangeNotifier { | |||||||
|     notifyListeners(); |     notifyListeners(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void removeLandmark(Landmark landmark) { |   void removeLandmark (Landmark landmark) async { | ||||||
|  |     Landmark? previous = landmark.previous; | ||||||
|  |     Landmark? next = landmark.next; | ||||||
|     landmarks.remove(landmark); |     landmarks.remove(landmark); | ||||||
|  |     // removing the landmark means we need to recompute the time between the two adjoined landmarks | ||||||
|  |     if (previous != null && next != null) { | ||||||
|  |       // previous.next = next happens automatically since we are using a LinkedList | ||||||
|  |       previous.tripTime = null; | ||||||
|  |       // TODO | ||||||
|  |     } | ||||||
|     notifyListeners(); |     notifyListeners(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user