Frontend UX improvements #37
| @@ -1,10 +1,12 @@ | ||||
| import 'package:anyway/utils/get_first_page.dart'; | ||||
| import 'package:anyway/utils/load_trips.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:anyway/constants.dart'; | ||||
|  | ||||
| void main() => runApp(const App()); | ||||
|  | ||||
| final GlobalKey<ScaffoldMessengerState> rootScaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>(); | ||||
| final SavedTrips savedTrips = SavedTrips(); | ||||
|  | ||||
| class App extends StatelessWidget { | ||||
|   const App({super.key}); | ||||
|   | ||||
| @@ -3,7 +3,6 @@ import 'package:anyway/main.dart'; | ||||
| import 'package:anyway/structs/trip.dart'; | ||||
| import 'package:auto_size_text/auto_size_text.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
|  | ||||
| class saveButton extends StatefulWidget { | ||||
| @@ -19,8 +18,9 @@ class _saveButtonState extends State<saveButton> { | ||||
|   Widget build(BuildContext context) { | ||||
|     return ElevatedButton( | ||||
|       onPressed: () async { | ||||
|         SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
|         setState(() => widget.trip.toPrefs(prefs)); | ||||
|         savedTrips.addTrip(widget.trip); | ||||
|         // SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
|         // setState(() => widget.trip.toPrefs(prefs)); | ||||
|         rootScaffoldMessengerKey.currentState!.showSnackBar( | ||||
|           SnackBar( | ||||
|             content: Text('Trip saved'), | ||||
|   | ||||
| @@ -1,11 +1,12 @@ | ||||
| import 'package:anyway/pages/current_trip.dart'; | ||||
| import 'package:anyway/utils/load_trips.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| import 'package:anyway/structs/trip.dart'; | ||||
|  | ||||
|  | ||||
| class TripsOverview extends StatefulWidget { | ||||
|   final Future<List<Trip>> trips; | ||||
|   final SavedTrips trips; | ||||
|   const TripsOverview({ | ||||
|     super.key, | ||||
|     required this.trips, | ||||
| @@ -16,49 +17,34 @@ class TripsOverview extends StatefulWidget { | ||||
| } | ||||
|  | ||||
| class _TripsOverviewState extends State<TripsOverview> { | ||||
|   Widget listBuild (BuildContext context, AsyncSnapshot<List<Trip>> snapshot) { | ||||
|   Widget listBuild (BuildContext context, SavedTrips trips) { | ||||
|     List<Widget> children; | ||||
|     if (snapshot.hasData) { | ||||
|       children = List<Widget>.generate(snapshot.data!.length, (index) { | ||||
|         Trip trip = snapshot.data![index]; | ||||
|         return ListTile( | ||||
|           title: FutureBuilder( | ||||
|             future: trip.cityName, | ||||
|             builder: (BuildContext context, AsyncSnapshot<String> snapshot) { | ||||
|               if (snapshot.hasData) { | ||||
|                 return Text("Trip to ${snapshot.data}"); | ||||
|               } else if (snapshot.hasError) { | ||||
|                 return Text("Error: ${snapshot.error}"); | ||||
|               } else { | ||||
|                 return const Text("Trip to ..."); | ||||
|               } | ||||
|             }, | ||||
|           ), | ||||
|           leading: Icon(Icons.pin_drop), | ||||
|           onTap: () { | ||||
|             Navigator.of(context).push( | ||||
|               MaterialPageRoute( | ||||
|                 builder: (context) => TripPage(trip: trip) | ||||
|               ) | ||||
|             ); | ||||
|     List<Trip> items = trips.trips; | ||||
|     children = List<Widget>.generate(items.length, (index) { | ||||
|       Trip trip = items[index]; | ||||
|       return ListTile( | ||||
|         title: FutureBuilder( | ||||
|           future: trip.cityName, | ||||
|           builder: (BuildContext context, AsyncSnapshot<String> snapshot) { | ||||
|             if (snapshot.hasData) { | ||||
|               return Text("Trip to ${snapshot.data}"); | ||||
|             } else if (snapshot.hasError) { | ||||
|               return Text("Error: ${snapshot.error}"); | ||||
|             } else { | ||||
|               return const Text("Trip to ..."); | ||||
|             } | ||||
|           }, | ||||
|         ); | ||||
|       }); | ||||
|     } else if (snapshot.hasError) { | ||||
|       children = [ | ||||
|         const Icon( | ||||
|           Icons.error_outline, | ||||
|           color: Colors.red, | ||||
|           size: 60, | ||||
|         ), | ||||
|         Padding( | ||||
|           padding: const EdgeInsets.only(top: 16), | ||||
|           child: Text('Error: ${snapshot.error}'), | ||||
|         ), | ||||
|       ]; | ||||
|     } else { | ||||
|       children = [Center(child: CircularProgressIndicator())]; | ||||
|     } | ||||
|         leading: Icon(Icons.pin_drop), | ||||
|         onTap: () { | ||||
|           Navigator.of(context).push( | ||||
|             MaterialPageRoute( | ||||
|               builder: (context) => TripPage(trip: trip) | ||||
|             ) | ||||
|           ); | ||||
|         }, | ||||
|       ); | ||||
|     }); | ||||
|  | ||||
|     return ListView( | ||||
|       children: children, | ||||
| @@ -68,9 +54,11 @@ class _TripsOverviewState extends State<TripsOverview> { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return FutureBuilder( | ||||
|       future: widget.trips, | ||||
|       builder: listBuild, | ||||
|     return ListenableBuilder( | ||||
|       listenable: widget.trips, | ||||
|       builder: (BuildContext context, Widget? child) { | ||||
|         return listBuild(context, widget.trips); | ||||
|       } | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| 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'; | ||||
| @@ -38,7 +39,8 @@ class _BasePageState extends State<BasePage> { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     Future<List<Trip>> trips = loadTrips(); | ||||
|     savedTrips.loadTrips(); | ||||
|  | ||||
|  | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
| @@ -98,11 +100,11 @@ class _BasePageState extends State<BasePage> { | ||||
|             // through the options in the drawer if there isn't enough vertical | ||||
|             // space to fit everything. | ||||
|             Expanded( | ||||
|               child: TripsOverview(trips: trips), | ||||
|               child: TripsOverview(trips: savedTrips), | ||||
|             ), | ||||
|             ElevatedButton( | ||||
|               onPressed: () async { | ||||
|                 removeAllTripsFromPrefs(); | ||||
|                 savedTrips.clearTrips(); | ||||
|               }, | ||||
|               child: const Text('Clear trips'), | ||||
|             ), | ||||
|   | ||||
| @@ -1,3 +1,6 @@ | ||||
| import 'dart:ui'; | ||||
|  | ||||
| import 'package:anyway/constants.dart'; | ||||
| import 'package:anyway/modules/onboarding_card.dart'; | ||||
| import 'package:anyway/pages/new_trip_location.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| @@ -33,32 +36,51 @@ class OnboardingPage extends StatefulWidget { | ||||
| } | ||||
|  | ||||
| class _OnboardingPageState extends State<OnboardingPage> { | ||||
|   final PageController _controller = PageController(); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final PageController _controller = PageController(); | ||||
|  | ||||
|     return Scaffold( | ||||
|       body: Stack( | ||||
|         children: [ | ||||
|           Container( | ||||
|             decoration: BoxDecoration( | ||||
|               gradient: LinearGradient( | ||||
|                 colors: [Colors.red, Colors.blue], | ||||
|                 begin: Alignment.topLeft, | ||||
|                 end: Alignment.bottomRight, | ||||
|               ), | ||||
|             ), | ||||
|             child: PageView( | ||||
|               controller: _controller, | ||||
|               children: List.generate( | ||||
|                 onboardingCards.length, | ||||
|                 (index) { | ||||
|                   return Container( | ||||
|                     alignment: Alignment.center, | ||||
|                     child: onboardingCards[index], | ||||
|                   ); | ||||
|                 } | ||||
|               ), | ||||
|           AnimatedBuilder( | ||||
|             animation: _controller, | ||||
|             builder: (context, child) { | ||||
|               return Stack( | ||||
|                 children: [ | ||||
|                   Container( | ||||
|                     decoration: BoxDecoration( | ||||
|                       gradient: LinearGradient( | ||||
|                         begin: Alignment.topLeft, | ||||
|                         end: Alignment.bottomRight, | ||||
|                         colors: APP_GRADIENT.colors, | ||||
|                         stops: [ | ||||
|                           (_controller.hasClients ? _controller.page ?? _controller.initialPage : _controller.initialPage) / onboardingCards.length, | ||||
|                           (_controller.hasClients ? _controller.page ?? _controller.initialPage + 1 : _controller.initialPage + 1) / onboardingCards.length, | ||||
|                         ], | ||||
|                       ), | ||||
|                     ), | ||||
|                   ), | ||||
|                   BackdropFilter( | ||||
|                     filter: ImageFilter.blur(sigmaX: 100, sigmaY: 100), | ||||
|                     child: Container( | ||||
|                       color: Colors.black.withOpacity(0), | ||||
|                     ), | ||||
|                   ), | ||||
|                 ], | ||||
|               ); | ||||
|             }, | ||||
|           ), | ||||
|           PageView( | ||||
|             controller: _controller, | ||||
|             children: List.generate( | ||||
|               onboardingCards.length, | ||||
|               (index) { | ||||
|                 return Container( | ||||
|                   alignment: Alignment.center, | ||||
|                   child: onboardingCards[index], | ||||
|                 ); | ||||
|               } | ||||
|             ), | ||||
|           ), | ||||
|         ], | ||||
| @@ -75,10 +97,10 @@ class _OnboardingPageState extends State<OnboardingPage> { | ||||
|             _controller.nextPage(duration: Duration(milliseconds: 500), curve: Curves.ease); | ||||
|           } | ||||
|         }, | ||||
|         label: ListenableBuilder( | ||||
|           listenable: _controller, | ||||
|         label: AnimatedBuilder( | ||||
|           animation: _controller, | ||||
|           builder: (context, child) { | ||||
|             if (_controller.page == onboardingCards.length - 1) { | ||||
|             if ((_controller.page ?? _controller.initialPage) == onboardingCards.length - 1) { | ||||
|               return Row( | ||||
|                 children: [ | ||||
|                   const Text("Start planning!"), | ||||
|   | ||||
| @@ -113,10 +113,3 @@ LinkedList<Landmark> readLandmarks(SharedPreferences prefs, String? firstUUID) { | ||||
|   } | ||||
|   return landmarks; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void removeAllTripsFromPrefs () async { | ||||
|   SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
|   prefs.clear(); | ||||
| } | ||||
|   | ||||
| @@ -5,23 +5,37 @@ import 'package:anyway/utils/load_trips.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| Widget getFirstPage() { | ||||
|   Future<List<Trip>> trips = loadTrips(); | ||||
|   // test if there are any active trips | ||||
|   // if there are, return the trip list | ||||
|   // if there are not, return the onboarding page | ||||
|   return FutureBuilder( | ||||
|     future: trips, | ||||
|     builder: (context, snapshot) { | ||||
|       if (snapshot.hasData) { | ||||
|         List<Trip> availableTrips = snapshot.data!; | ||||
|         if (availableTrips.isNotEmpty) { | ||||
|           return TripPage(trip: availableTrips[0]); | ||||
|         } else { | ||||
|           return OnboardingPage(); | ||||
|         } | ||||
|   SavedTrips trips = SavedTrips(); | ||||
|   trips.loadTrips(); | ||||
|  | ||||
|   return ListenableBuilder( | ||||
|     listenable: trips, | ||||
|     builder: (BuildContext context, Widget? child) { | ||||
|       List<Trip> items = trips.trips; | ||||
|       if (items.isNotEmpty) { | ||||
|         return TripPage(trip: items[0]); | ||||
|       } else { | ||||
|         return CircularProgressIndicator(); | ||||
|         return OnboardingPage(); | ||||
|       } | ||||
|     } | ||||
|   ); | ||||
|   // Future<List<Trip>> trips = loadTrips(); | ||||
|   // // test if there are any active trips | ||||
|   // // if there are, return the trip list | ||||
|   // // if there are not, return the onboarding page | ||||
|   // return FutureBuilder( | ||||
|   //   future: trips, | ||||
|   //   builder: (context, snapshot) { | ||||
|   //     if (snapshot.hasData) { | ||||
|   //       List<Trip> availableTrips = snapshot.data!; | ||||
|   //       if (availableTrips.isNotEmpty) { | ||||
|   //         return TripPage(trip: availableTrips[0]); | ||||
|   //       } else { | ||||
|   //         return OnboardingPage(); | ||||
|   //       } | ||||
|   //     } else { | ||||
|   //       return CircularProgressIndicator(); | ||||
|   //     } | ||||
|   //   } | ||||
|   // ); | ||||
| } | ||||
| @@ -1,16 +1,39 @@ | ||||
| import 'package:anyway/structs/trip.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
| Future<List<Trip>> loadTrips() async { | ||||
|   SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
| import 'package:flutter/foundation.dart'; | ||||
|  | ||||
|   List<Trip> trips = []; | ||||
|   Set<String> keys = prefs.getKeys(); | ||||
|   for (String key in keys) { | ||||
|     if (key.startsWith('trip_')) { | ||||
|       String uuid = key.replaceFirst('trip_', ''); | ||||
|       trips.add(Trip.fromPrefs(prefs, uuid)); | ||||
| class SavedTrips extends ChangeNotifier { | ||||
|   List<Trip> _trips = []; | ||||
|  | ||||
|   List<Trip> get trips => _trips; | ||||
|  | ||||
|   void loadTrips() async { | ||||
|     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
|  | ||||
|     List<Trip> trips = []; | ||||
|     Set<String> keys = prefs.getKeys(); | ||||
|     for (String key in keys) { | ||||
|       if (key.startsWith('trip_')) { | ||||
|         String uuid = key.replaceFirst('trip_', ''); | ||||
|         trips.add(Trip.fromPrefs(prefs, uuid)); | ||||
|       } | ||||
|     } | ||||
|     _trips = trips; | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   void addTrip(Trip trip) async { | ||||
|     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
|     trip.toPrefs(prefs); | ||||
|     _trips.add(trip); | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   void clearTrips () async { | ||||
|     SharedPreferences prefs = await SharedPreferences.getInstance(); | ||||
|     prefs.clear(); | ||||
|     _trips = []; | ||||
|     notifyListeners(); | ||||
|   } | ||||
|   return trips; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user