overhaul using a trip struct that notifies its ui dependencies
This commit is contained in:
		| @@ -3,12 +3,10 @@ import 'package:anyway/structs/trip.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class Greeter extends StatefulWidget { | ||||
|   final Future<Trip> trip; | ||||
|   final bool standalone; | ||||
|   final Trip trip; | ||||
|  | ||||
|   Greeter({ | ||||
|     required this.standalone, | ||||
|     required this.trip | ||||
|     required this.trip, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
| @@ -18,55 +16,66 @@ class Greeter extends StatefulWidget { | ||||
|  | ||||
|  | ||||
| class _GreeterState extends State<Greeter> { | ||||
|   Widget greeterBuild (BuildContext context, AsyncSnapshot<Trip> snapshot) { | ||||
|   Widget greeterBuilder (BuildContext context, Widget? child) { | ||||
|     ThemeData theme = Theme.of(context); | ||||
|     Widget topGreeter; | ||||
|     if (snapshot.hasData) { | ||||
|       topGreeter = Padding( | ||||
|         padding: const EdgeInsets.only(top: 20, bottom: 20), | ||||
|         child: Text( | ||||
|           'Welcome to ${snapshot.data?.cityName}!', | ||||
|           style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24), | ||||
|         ) | ||||
|       ); | ||||
|     } else if (snapshot.hasError) { | ||||
|       topGreeter = const Padding( | ||||
|         padding: EdgeInsets.only(top: 20, bottom: 20), | ||||
|         child: Text('Error while fetching trip') | ||||
|     if (widget.trip.landmarks.length > 1) { | ||||
|       topGreeter = FutureBuilder( | ||||
|         future: widget.trip.cityName, | ||||
|         builder: (BuildContext context, AsyncSnapshot<String> snapshot) { | ||||
|           if (snapshot.hasData) { | ||||
|             return Text( | ||||
|               'Welcome to ${snapshot.data}!', | ||||
|               style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24), | ||||
|             ); | ||||
|           } else if (snapshot.hasError) { | ||||
|             return const Text('Welcome to your trip!'); | ||||
|           } else { | ||||
|             return const Text('Welcome to ...'); | ||||
|           } | ||||
|         } | ||||
|       ); | ||||
|     } else { | ||||
|       // still awaiting the cityname | ||||
|       // still awaiting the trip | ||||
|       // We can hopefully infer the city name from the cityName future | ||||
|       // Show a linear loader at the bottom and an info message above | ||||
|       topGreeter = Column( | ||||
|         mainAxisAlignment: MainAxisAlignment.end, | ||||
|         children: [ | ||||
|           Padding( | ||||
|             padding: const EdgeInsets.only(top: 20, bottom: 20), | ||||
|             child: const Text('Generating your trip...', style: TextStyle(fontSize: 20),) | ||||
|           FutureBuilder( | ||||
|             future: widget.trip.cityName, | ||||
|             builder: (BuildContext context, AsyncSnapshot<String> snapshot) { | ||||
|               if (snapshot.hasData) { | ||||
|                 return Text( | ||||
|                   'Generating your trip to ${snapshot.data}...', | ||||
|                   style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24), | ||||
|                 ); | ||||
|               } else if (snapshot.hasError) { | ||||
|                 return const Text('Error while fetching city name'); | ||||
|               } | ||||
|               return const Text('Generating your trip...'); | ||||
|             } | ||||
|           ), | ||||
|           const LinearProgressIndicator() | ||||
|           Padding( | ||||
|             padding: EdgeInsets.all(5), | ||||
|             child: const LinearProgressIndicator() | ||||
|           ) | ||||
|         ] | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|      | ||||
|  | ||||
|     if (widget.standalone) { | ||||
|       return Center( | ||||
|         child: topGreeter, | ||||
|       ); | ||||
|     } else { | ||||
|       return Center( | ||||
|         child: Column( | ||||
|           children: [ | ||||
|             Padding(padding: EdgeInsets.only(top: 24.0)), | ||||
|             topGreeter, | ||||
|             bottomGreeter, | ||||
|             Padding(padding: EdgeInsets.only(bottom: 24.0)), | ||||
|           ], | ||||
|         ) | ||||
|       ); | ||||
|     } | ||||
|     return Center( | ||||
|       child: Column( | ||||
|         children: [ | ||||
|           // Padding(padding: EdgeInsets.only(top: 20)), | ||||
|           topGreeter, | ||||
|           Padding( | ||||
|             padding: EdgeInsets.all(20), | ||||
|             child: bottomGreeter | ||||
|           ), | ||||
|         ], | ||||
|       ) | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget bottomGreeter = const Text( | ||||
| @@ -79,9 +88,9 @@ class _GreeterState extends State<Greeter> { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return FutureBuilder( | ||||
|       future: widget.trip, | ||||
|       builder: greeterBuild, | ||||
|     return ListenableBuilder( | ||||
|       listenable: widget.trip, | ||||
|       builder: greeterBuilder, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,4 +1,5 @@ | ||||
| import 'package:anyway/structs/landmark.dart'; | ||||
| import 'package:cached_network_image/cached_network_image.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
|  | ||||
| @@ -31,9 +32,10 @@ class _LandmarkCardState extends State<LandmarkCard> { | ||||
|               height: double.infinity, | ||||
|               // force a fixed width | ||||
|               width: 160, | ||||
|               child: Image.network( | ||||
|                 widget.landmark.imageURL ?? '', | ||||
|                 errorBuilder: (context, error, stackTrace) => Icon(Icons.question_mark_outlined), | ||||
|               child: CachedNetworkImage( | ||||
|                 imageUrl: widget.landmark.imageURL ?? '', | ||||
|                 placeholder: (context, url) => CircularProgressIndicator(), | ||||
|                 errorWidget: (context, error, stackTrace) => Icon(Icons.question_mark_outlined), | ||||
|                 // TODO: make this a switch statement to load a placeholder if null | ||||
|                 // cover the whole container meaning the image will be cropped | ||||
|                 fit: BoxFit.cover, | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| import 'dart:collection'; | ||||
| import 'dart:developer'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
| import 'package:anyway/modules/landmark_card.dart'; | ||||
| import 'package:anyway/structs/landmark.dart'; | ||||
|  | ||||
| import 'package:anyway/structs/trip.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| class LandmarksOverview extends StatefulWidget { | ||||
|   final Future<Trip>? trip; | ||||
|   final Trip? trip; | ||||
|   const LandmarksOverview({super.key, this.trip}); | ||||
|  | ||||
|   @override | ||||
| @@ -23,18 +23,17 @@ class _LandmarksOverviewState extends State<LandmarksOverview> { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final Future<LinkedList<Landmark>> _landmarks = getLandmarks(widget.trip); | ||||
|     return DefaultTextStyle( | ||||
|       style: Theme.of(context).textTheme.displayMedium!, | ||||
|       textAlign: TextAlign.center, | ||||
|       child: FutureBuilder<LinkedList<Landmark>>( | ||||
|         future: _landmarks, | ||||
|         builder: (BuildContext context, AsyncSnapshot<LinkedList<Landmark>> snapshot) { | ||||
|           List<Widget> children; | ||||
|           if (snapshot.hasData) { | ||||
|             children = [landmarksWithSteps(snapshot.data!), saveButton()]; | ||||
|           } else if (snapshot.hasError) { | ||||
|             children = <Widget>[ | ||||
|     return ListenableBuilder(//<LinkedList<Landmark>> | ||||
|       listenable: widget.trip!, | ||||
|       builder: (BuildContext context, Widget? child) { | ||||
|         Trip trip = widget.trip!; | ||||
|         log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks"); | ||||
|         List<Widget> children; | ||||
|         if (trip.uuid == 'pending') { | ||||
|           // the trip is still being fetched from the api | ||||
|           children = [Center(child: CircularProgressIndicator())]; | ||||
|         } else if (trip.uuid == 'error') { | ||||
|             children = [ | ||||
|               const Icon( | ||||
|                 Icons.error_outline, | ||||
|                 color: Colors.red, | ||||
| @@ -42,20 +41,25 @@ class _LandmarksOverviewState extends State<LandmarksOverview> { | ||||
|               ), | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.only(top: 16), | ||||
|                 child: Text('Error: ${snapshot.error}', style: TextStyle(fontSize: 12)), | ||||
|                 child: Text('Error: ${trip.cityName}'), | ||||
|               ), | ||||
|             ]; | ||||
|         } else { | ||||
|           if (trip.landmarks.length <= 1) { | ||||
|             children = [ | ||||
|               const Text("No landmarks in this trip"), | ||||
|             ]; | ||||
|           } else { | ||||
|             children = [Center(child: CircularProgressIndicator())]; | ||||
|             children = [ | ||||
|               landmarksWithSteps(trip.landmarks), | ||||
|               saveButton(), | ||||
|             ]; | ||||
|           } | ||||
|           return Center( | ||||
|             child: Column( | ||||
|               mainAxisAlignment: MainAxisAlignment.center, | ||||
|               children: children, | ||||
|             ), | ||||
|           ); | ||||
|         }, | ||||
|       ), | ||||
|         } | ||||
|         return Column( | ||||
|           children: children, | ||||
|         ); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
|   Widget saveButton() => ElevatedButton( | ||||
| @@ -100,7 +104,7 @@ Widget landmarksWithSteps(LinkedList<Landmark> landmarks) { | ||||
|     ); | ||||
|     lkey++; | ||||
|     if (landmark.next != null) { | ||||
|       Widget step = stepBetweenLandmarks(landmark, landmark.next!); | ||||
|       Widget step = stepBetweenLandmarks(landmark); | ||||
|       children.add(step); | ||||
|     } | ||||
|   } | ||||
| @@ -111,7 +115,7 @@ Widget landmarksWithSteps(LinkedList<Landmark> landmarks) { | ||||
| } | ||||
|  | ||||
|  | ||||
| Widget stepBetweenLandmarks(Landmark before, Landmark after) { | ||||
| Widget stepBetweenLandmarks(Landmark landmark) { | ||||
|   // This is a simple widget that draws a line between landmark-cards | ||||
|   // It's a vertical dotted line | ||||
|   // Next to the line is the icon for the mode of transport (walking for now) and the estimated time | ||||
| @@ -134,7 +138,7 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) { | ||||
|         Column( | ||||
|           children: [ | ||||
|             Icon(Icons.directions_walk), | ||||
|             Text("5 min", style: TextStyle(fontSize: 10)), | ||||
|             Text("${landmark.tripTime} min", style: TextStyle(fontSize: 10)), | ||||
|           ], | ||||
|         ), | ||||
|         Spacer(), | ||||
| @@ -149,8 +153,5 @@ Widget stepBetweenLandmarks(Landmark before, Landmark after) { | ||||
|   ); | ||||
| } | ||||
|  | ||||
| Future<LinkedList<Landmark>> getLandmarks (Future<Trip>? trip) async { | ||||
|   Trip tripf = await trip!; | ||||
|   return tripf.landmarks; | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import 'package:the_widget_marker/the_widget_marker.dart'; | ||||
|  | ||||
| class MapWidget extends StatefulWidget { | ||||
|  | ||||
|   final Future<Trip>? trip; | ||||
|   final Trip? trip; | ||||
|  | ||||
|   MapWidget({ | ||||
|     this.trip | ||||
| @@ -31,8 +31,7 @@ class _MapWidgetState extends State<MapWidget> { | ||||
|  | ||||
|   void _onMapCreated(GoogleMapController controller) async { | ||||
|     mapController = controller; | ||||
|     Trip? trip = await widget.trip; | ||||
|     List<double>? newLocation = trip?.landmarks.first.location; | ||||
|     List<double>? newLocation = widget.trip?.landmarks.first.location; | ||||
|     if (newLocation != null) { | ||||
|       CameraUpdate update = CameraUpdate.newLatLng(LatLng(newLocation[0], newLocation[1])); | ||||
|       controller.moveCamera(update); | ||||
| @@ -48,8 +47,7 @@ class _MapWidgetState extends State<MapWidget> { | ||||
|  | ||||
|   void drawLandmarks() async { | ||||
|     // (re)draws landmarks on the map | ||||
|     Trip? trip = await widget.trip; | ||||
|     LinkedList<Landmark>? landmarks = trip?.landmarks; | ||||
|     LinkedList<Landmark>? landmarks = widget.trip?.landmarks; | ||||
|     if (landmarks != null){ | ||||
|       for (Landmark landmark in landmarks) { | ||||
|         markers.add(Marker( | ||||
|   | ||||
| @@ -30,7 +30,7 @@ class _TripsOverviewState extends State<TripsOverview> { | ||||
|           onTap: () { | ||||
|             Navigator.of(context).push( | ||||
|               MaterialPageRoute( | ||||
|                 builder: (context) => BasePage(mainScreen: "map", trip: Future.value(trip)) | ||||
|                 builder: (context) => BasePage(mainScreen: "map", trip: trip) | ||||
|               ) | ||||
|             ); | ||||
|           }, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user