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