Update pull down gesture to be much smoother, refactor home page routing, load weather from OpenWeatherMap
This commit is contained in:
		| @@ -45,5 +45,7 @@ | ||||
| 	<false/> | ||||
| 	<key>CADisableMinimumFrameDurationOnPhone</key> | ||||
| 	<true/> | ||||
| 	<key>NSLocationWhenInUseUsageDescription</key> | ||||
| 	<string>Your location will be used to provide weather and map data.</string> | ||||
| </dict> | ||||
| </plist> | ||||
|   | ||||
| @@ -1,10 +1,10 @@ | ||||
| import 'package:auto_route/auto_route.dart'; | ||||
| import 'package:furman_now/src/screens/events/index.dart'; | ||||
| import 'package:furman_now/src/screens/home/home_header.dart'; | ||||
| import 'package:furman_now/src/screens/home/index.dart'; | ||||
| import 'package:furman_now/src/screens/info/index.dart'; | ||||
| import 'package:furman_now/src/screens/map/index.dart'; | ||||
| import 'package:furman_now/src/screens/student_id/index.dart'; | ||||
| import 'package:furman_now/src/utils/hero_empty_router_page.dart'; | ||||
|  | ||||
| import '../layouts/main/index.dart'; | ||||
|  | ||||
| @@ -12,16 +12,17 @@ import '../layouts/main/index.dart'; | ||||
|   replaceInRouteName: 'Screen,Route', | ||||
|   routes: <AutoRoute>[ | ||||
|     AutoRoute(path: "/", page: MainLayout, children: [ | ||||
|       AutoRoute(path: "home", name: "HomePageRouter", page: HeroEmptyRouterPage, children: [ | ||||
|       AutoRoute(path: "home", name: "HomePageRouter", page: HomeScreen, children: [ | ||||
|         CustomRoute( | ||||
|           path: "", | ||||
|           page: HomeScreen, | ||||
|           transitionsBuilder: TransitionsBuilders.noTransition, | ||||
|           page: HomePageHeader, | ||||
|           name: "HomeRoute", | ||||
|           transitionsBuilder: TransitionsBuilders.fadeIn, | ||||
|         ), | ||||
|         CustomRoute( | ||||
|           path: "student-id", | ||||
|           page: StudentIdScreen, | ||||
|           transitionsBuilder: TransitionsBuilders.noTransition, | ||||
|           transitionsBuilder: TransitionsBuilders.fadeIn, | ||||
|         ), | ||||
|       ]), | ||||
|       AutoRoute(path: "map", page: MapScreen), | ||||
|   | ||||
| @@ -16,11 +16,11 @@ import 'package:flutter/material.dart' as _i9; | ||||
|  | ||||
| import '../layouts/main/index.dart' as _i1; | ||||
| import '../screens/events/index.dart' as _i4; | ||||
| import '../screens/home/index.dart' as _i6; | ||||
| import '../screens/home/home_header.dart' as _i6; | ||||
| import '../screens/home/index.dart' as _i2; | ||||
| import '../screens/info/index.dart' as _i5; | ||||
| import '../screens/map/index.dart' as _i3; | ||||
| import '../screens/student_id/index.dart' as _i7; | ||||
| import '../utils/hero_empty_router_page.dart' as _i2; | ||||
|  | ||||
| class AppRouter extends _i8.RootStackRouter { | ||||
|   AppRouter([_i9.GlobalKey<_i9.NavigatorState>? navigatorKey]) | ||||
| @@ -34,7 +34,7 @@ class AppRouter extends _i8.RootStackRouter { | ||||
|     }, | ||||
|     HomePageRouter.name: (routeData) { | ||||
|       return _i8.MaterialPageX<dynamic>( | ||||
|           routeData: routeData, child: const _i2.HeroEmptyRouterPage()); | ||||
|           routeData: routeData, child: const _i2.HomeScreen()); | ||||
|     }, | ||||
|     MapRoute.name: (routeData) { | ||||
|       return _i8.MaterialPageX<dynamic>( | ||||
| @@ -51,8 +51,8 @@ class AppRouter extends _i8.RootStackRouter { | ||||
|     HomeRoute.name: (routeData) { | ||||
|       return _i8.CustomPage<dynamic>( | ||||
|           routeData: routeData, | ||||
|           child: const _i6.HomeScreen(), | ||||
|           transitionsBuilder: _i8.TransitionsBuilders.noTransition, | ||||
|           child: const _i6.HomePageHeader(), | ||||
|           transitionsBuilder: _i8.TransitionsBuilders.fadeIn, | ||||
|           opaque: true, | ||||
|           barrierDismissible: false); | ||||
|     }, | ||||
| @@ -60,7 +60,7 @@ class AppRouter extends _i8.RootStackRouter { | ||||
|       return _i8.CustomPage<dynamic>( | ||||
|           routeData: routeData, | ||||
|           child: const _i7.StudentIdScreen(), | ||||
|           transitionsBuilder: _i8.TransitionsBuilders.noTransition, | ||||
|           transitionsBuilder: _i8.TransitionsBuilders.fadeIn, | ||||
|           opaque: true, | ||||
|           barrierDismissible: false); | ||||
|     } | ||||
| @@ -96,7 +96,7 @@ class MainLayout extends _i8.PageRouteInfo<void> { | ||||
| } | ||||
|  | ||||
| /// generated route for | ||||
| /// [_i2.HeroEmptyRouterPage] | ||||
| /// [_i2.HomeScreen] | ||||
| class HomePageRouter extends _i8.PageRouteInfo<void> { | ||||
|   const HomePageRouter({List<_i8.PageRouteInfo>? children}) | ||||
|       : super(HomePageRouter.name, path: 'home', initialChildren: children); | ||||
| @@ -129,7 +129,7 @@ class InfoRoute extends _i8.PageRouteInfo<void> { | ||||
| } | ||||
|  | ||||
| /// generated route for | ||||
| /// [_i6.HomeScreen] | ||||
| /// [_i6.HomePageHeader] | ||||
| class HomeRoute extends _i8.PageRouteInfo<void> { | ||||
|   const HomeRoute() : super(HomeRoute.name, path: ''); | ||||
|  | ||||
|   | ||||
							
								
								
									
										107
									
								
								lib/src/screens/home/content.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								lib/src/screens/home/content.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:furman_now/src/routes/index.gr.dart'; | ||||
| import 'package:furman_now/src/utils/conditional_parent_widget.dart'; | ||||
| import 'package:furman_now/src/widgets/events/events_list.dart'; | ||||
| import 'package:furman_now/src/widgets/header.dart'; | ||||
| import 'package:furman_now/src/widgets/home/restaurants/restaurants_list.dart'; | ||||
| import 'package:furman_now/src/widgets/home/transportation/transportation_card.dart'; | ||||
| import 'package:furman_now/src/widgets/scroll_view_height.dart'; | ||||
|  | ||||
| class HomeContent extends StatefulWidget { | ||||
|   final bool collapse; | ||||
|  | ||||
|   const HomeContent({ | ||||
|     required this.collapse, | ||||
|     Key? key, | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   State<HomeContent> createState() => _HomeContentState(); | ||||
| } | ||||
|  | ||||
| class _HomeContentState extends State<HomeContent> { | ||||
|   final ScrollController _controller = ScrollController(); | ||||
|   bool _collapse = false; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     if (widget.collapse != _collapse) { | ||||
|       _controller.animateTo( | ||||
|         0, | ||||
|         duration: const Duration(milliseconds: 400), | ||||
|         curve: Curves.easeInOut, | ||||
|       ); | ||||
|       _collapse = widget.collapse; | ||||
|     } | ||||
|  | ||||
|     return LayoutBuilder( | ||||
|       builder: (context, constraints) => ScrollViewWithHeight( | ||||
|         controller: _controller, | ||||
|         child: Align( | ||||
|           alignment: Alignment.bottomCenter, | ||||
|           child: TweenAnimationBuilder<double>( | ||||
|             tween: widget.collapse | ||||
|               ? Tween<double>(begin: 200, end: constraints.maxHeight - 75) | ||||
|               : Tween<double>(begin: constraints.maxHeight - 75, end: 200), | ||||
|             curve: Curves.easeInOut, | ||||
|             duration: const Duration(milliseconds: 200), | ||||
|             builder: (context, margin, _) => Stack( | ||||
|               children: [ | ||||
|                 Container( | ||||
|                   decoration: const BoxDecoration( | ||||
|                     color: Colors.white, | ||||
|                     borderRadius: | ||||
|                     BorderRadius.vertical(top: Radius.circular(30)), | ||||
|                   ), | ||||
|                   padding: const EdgeInsets.only(bottom: 15), | ||||
|                   margin: EdgeInsets.only(top: margin), | ||||
|                   width: double.infinity, | ||||
|                   child: Column( | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: [ | ||||
|                       AnimatedContainer( | ||||
|                         duration: const Duration(milliseconds: 300), | ||||
|                         height: widget.collapse ? 55 : 0, | ||||
|                         child: Align( | ||||
|                           alignment: Alignment.topCenter, | ||||
|                           child: Container( | ||||
|                               margin: const EdgeInsets.symmetric(vertical: 15), | ||||
|                               width: 60, | ||||
|                               height: 5, | ||||
|                               decoration: BoxDecoration( | ||||
|                                 color: Colors.grey[300], | ||||
|                                 borderRadius: BorderRadius.circular(20), | ||||
|                               ) | ||||
|                           ), | ||||
|                         ), | ||||
|                       ), | ||||
|                       const SizedBox(height: 20), | ||||
|                       const HeaderWidget( | ||||
|                         title: "Today's Events", | ||||
|                         link: HeaderLink( | ||||
|                             text: "View more", | ||||
|                             href: EventsRoute() | ||||
|                         ), | ||||
|                       ), | ||||
|                       Padding( | ||||
|                         padding: const EdgeInsets.symmetric(horizontal: 20), | ||||
|                         child: EventsList(), | ||||
|                       ), | ||||
|                       const HeaderWidget(title: "Food & Dining"), | ||||
|                       const RestaurantsList(), | ||||
|                       const HeaderWidget(title: "Transportation"), | ||||
|                       const Padding( | ||||
|                         padding: EdgeInsets.symmetric(horizontal: 20), | ||||
|                         child: TransportationCard(), | ||||
|                       ), | ||||
|                     ], | ||||
|                   ), | ||||
|                 ), | ||||
|               ] | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										92
									
								
								lib/src/screens/home/home_header.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								lib/src/screens/home/home_header.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:furman_now/src/screens/home/state.dart'; | ||||
| import 'package:furman_now/src/services/weather/weather_service.dart'; | ||||
| import 'package:furman_now/src/utils/greeting.dart'; | ||||
| import 'package:furman_now/src/utils/theme.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class HomePageHeader extends StatelessWidget { | ||||
|   const HomePageHeader({ Key? key }) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container( | ||||
|       width: double.infinity, | ||||
|       height: 300, | ||||
|       decoration: const BoxDecoration( | ||||
|         gradient: LinearGradient( | ||||
|           begin: Alignment.topCenter, | ||||
|           end: Alignment.bottomCenter, | ||||
|           colors: <Color>[ | ||||
|             Color(0xffb7acc9), | ||||
|             Color(0xffb7acc9), | ||||
|           ], | ||||
|           tileMode: TileMode.mirror, | ||||
|         ), | ||||
|       ), | ||||
|       child: Align( | ||||
|         alignment: Alignment.topLeft, | ||||
|         child: Container( | ||||
|           padding: const EdgeInsets.all(40), | ||||
|           width: double.infinity, | ||||
|           height: 200, | ||||
|           child: Column( | ||||
|             mainAxisAlignment: MainAxisAlignment.center, | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|               Hero( | ||||
|                 tag: "title", | ||||
|                 child: Material( | ||||
|                   type: MaterialType.transparency, | ||||
|                   child: Text( | ||||
|                     "${greeting()},\nMichael", | ||||
|                     style: furmanTextStyle(const TextStyle( | ||||
|                       color: Color(0xff26183d), | ||||
|                       fontSize: 36, | ||||
|                       fontWeight: FontWeight.w800, | ||||
|                     )), | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|               const SizedBox(height: 8), | ||||
|               Consumer<HomePageState>( | ||||
|                 builder: (context, state, _) => AnimatedCrossFade( | ||||
|                   crossFadeState: | ||||
|                     state.showScrollMessage | ||||
|                       ? CrossFadeState.showSecond | ||||
|                       : CrossFadeState.showFirst, | ||||
|                   duration: const Duration(milliseconds: 100), | ||||
|                   firstChild: FutureBuilder<String>( | ||||
|                     future: WeatherService.getWeatherMessage(), | ||||
|                     builder: (context, snapshot) { | ||||
|                       if(snapshot.hasData) { | ||||
|                         return Text( | ||||
|                           snapshot.data!, | ||||
|                           // "It's 76º and partly cloudy", | ||||
|                           style: furmanTextStyle(const TextStyle | ||||
|                           ( | ||||
|                             color: Color(0xff26183d), | ||||
|                             fontSize: 16, | ||||
|                             fontWeight: FontWeight.w500, | ||||
|                           )), | ||||
|                         ); | ||||
|                       } | ||||
|                       return const SizedBox(height: 0); | ||||
|                     } | ||||
|                   ), | ||||
|                   secondChild: Text("Pull down to view your Meal ID", | ||||
|                     style: furmanTextStyle(const TextStyle( | ||||
|                         color: Color(0xff26183d), | ||||
|                         fontSize: 16, | ||||
|                         fontWeight: FontWeight.w500 | ||||
|                     )), | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,13 +1,9 @@ | ||||
| import 'package:auto_route/auto_route.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:furman_now/src/routes/index.gr.dart'; | ||||
| import 'package:furman_now/src/utils/greeting.dart'; | ||||
| import 'package:furman_now/src/utils/theme.dart'; | ||||
| import 'package:furman_now/src/widgets/header.dart'; | ||||
| import 'package:furman_now/src/widgets/events/events_list.dart'; | ||||
| import 'package:furman_now/src/widgets/home/restaurants/restaurants_list.dart'; | ||||
| import 'package:furman_now/src/widgets/home/transportation/transportation_card.dart'; | ||||
| import 'package:furman_now/src/widgets/scroll_view_height.dart'; | ||||
| import 'package:furman_now/src/screens/home/content.dart'; | ||||
| import 'package:furman_now/src/screens/home/state.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class HomeScreen extends StatefulWidget { | ||||
|   const HomeScreen({Key? key}) : super(key: key); | ||||
| @@ -17,151 +13,71 @@ class HomeScreen extends StatefulWidget { | ||||
| } | ||||
|  | ||||
| class _HomeScreenState extends State<HomeScreen> { | ||||
|   CrossFadeState _showScrollMessage = CrossFadeState.showFirst; | ||||
|   bool _collapse = false; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       body: Container( | ||||
|         color: const Color(0xffb7acc9), | ||||
|         child: SafeArea( | ||||
|           child: Container( | ||||
|             color: Colors.grey[100], | ||||
|             child: Stack( | ||||
|               fit: StackFit.loose, | ||||
|               children: [ | ||||
|                 Container( | ||||
|                   width: double.infinity, | ||||
|                   height: 300, | ||||
|                   decoration: const BoxDecoration( | ||||
|                     gradient: LinearGradient( | ||||
|                       begin: Alignment.topCenter, | ||||
|                       end: Alignment.bottomCenter, | ||||
|                       colors: <Color>[ | ||||
|                         Color(0xffb7acc9), | ||||
|                         Color(0xffb7acc9), | ||||
|                       ], | ||||
|                       tileMode: TileMode.mirror, | ||||
|                     ), | ||||
|     return ChangeNotifierProvider( | ||||
|       create: (context) => HomePageState(), | ||||
|       builder: (context, _) => Scaffold( | ||||
|         body: Container( | ||||
|           color: const Color(0xffb7acc9), | ||||
|           child: SafeArea( | ||||
|             child: Container( | ||||
|               color: const Color(0xffb7acc9), | ||||
|               child: Stack( | ||||
|                 fit: StackFit.loose, | ||||
|                 children: [ | ||||
|                   const AutoTabsRouter( | ||||
|                     routes: [ | ||||
|                       HomeRoute(), | ||||
|                       StudentIdRoute(), | ||||
|                     ], | ||||
|                   ), | ||||
|                   child: Align( | ||||
|                     alignment: Alignment.topLeft, | ||||
|                     child: Container( | ||||
|                       padding: const EdgeInsets.all(40), | ||||
|                       width: double.infinity, | ||||
|                       height: 200, | ||||
|                       child: Column( | ||||
|                         mainAxisAlignment: MainAxisAlignment.center, | ||||
|                         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                         children: [ | ||||
|                           Hero( | ||||
|                             tag: "title", | ||||
|                             child: Material( | ||||
|                               type: MaterialType.transparency, | ||||
|                               child: Text( | ||||
|                                 "${greeting()},\nMichael", | ||||
|                                 style: furmanTextStyle(const TextStyle( | ||||
|                                   color: Color(0xff26183d), | ||||
|                                   fontSize: 36, | ||||
|                                   fontWeight: FontWeight.w800, | ||||
|                                 )), | ||||
|                               ), | ||||
|                             ), | ||||
|                           ), | ||||
|                           const SizedBox(height: 5), | ||||
|                           AnimatedCrossFade( | ||||
|                             crossFadeState: _showScrollMessage, | ||||
|                             duration: const Duration(milliseconds: 100), | ||||
|                             firstChild: Text("It's 76º and partly cloudy", | ||||
|                               style: furmanTextStyle(const TextStyle( | ||||
|                                 color: Color(0xff26183d), | ||||
|                                 fontSize: 16, | ||||
|                                 fontWeight: FontWeight.w500) | ||||
|                               ), | ||||
|                             ), | ||||
|                             secondChild: Text("Pull down to view your Meal ID", | ||||
|                               style: furmanTextStyle(const TextStyle( | ||||
|                                   color: Color(0xff26183d), | ||||
|                                   fontSize: 16, | ||||
|                                   fontWeight: FontWeight.w500) | ||||
|                               ), | ||||
|                             ), | ||||
|                           ), | ||||
|                         ], | ||||
|                       ), | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|                 NotificationListener<ScrollNotification>( | ||||
|                   onNotification: (notification) { | ||||
|                     if (notification is ScrollUpdateNotification) { | ||||
|                       final offset = notification.metrics.pixels; | ||||
|                       if (offset < 0) { | ||||
|                         var offsetAmount = offset.abs(); | ||||
|                         if (offsetAmount > 50) { | ||||
|                           context.router.navigate(const StudentIdRoute()); | ||||
|                         } else if (offsetAmount > 20) { | ||||
|                           setState(() { | ||||
|                             _showScrollMessage = CrossFadeState.showSecond; | ||||
|                           }); | ||||
|                   NotificationListener<ScrollNotification>( | ||||
|                     onNotification: (notification) { | ||||
|                       if (notification is ScrollUpdateNotification) { | ||||
|                         final offset = notification.metrics.pixels; | ||||
|                         const scrollMessageSensitivity = 20; | ||||
|                         const pageSwitchSensitivity = 60; | ||||
|                         if (offset < 0) { | ||||
|                           var offsetAmount = offset.abs(); | ||||
|                           if (offsetAmount > pageSwitchSensitivity) { | ||||
|                             context.router.navigate(const StudentIdRoute()); | ||||
|                             setState(() { | ||||
|                               _collapse = true; | ||||
|                             }); | ||||
|                           } else if (offsetAmount > scrollMessageSensitivity) { | ||||
|                             if (!context.read<HomePageState>().showScrollMessage) { | ||||
|                               context.read<HomePageState>().showScrollMessage = true; | ||||
|                             } | ||||
|                           } else { | ||||
|                             if (context.read<HomePageState>().showScrollMessage) { | ||||
|                               context.read<HomePageState>().showScrollMessage = false; | ||||
|                             } | ||||
|                           } | ||||
|                         } else { | ||||
|                           setState(() { | ||||
|                             _showScrollMessage = CrossFadeState.showFirst; | ||||
|                           }); | ||||
|                         } | ||||
|                       } else { | ||||
|                         if (_showScrollMessage != CrossFadeState.showFirst) { | ||||
|                           setState(() { | ||||
|                             _showScrollMessage = CrossFadeState.showFirst; | ||||
|                           }); | ||||
|                           var offsetAmount = offset.abs(); | ||||
|                           if (offsetAmount > pageSwitchSensitivity) { | ||||
|                             context.router.navigate(const HomeRoute()); | ||||
|                             setState(() { | ||||
|                               _collapse = false; | ||||
|                             }); | ||||
|                           } else { | ||||
|                             if (context.read<HomePageState>().showScrollMessage) { | ||||
|                               context.read<HomePageState>().showScrollMessage = false; | ||||
|                             } | ||||
|                           } | ||||
|                         } | ||||
|                       } | ||||
|                     } | ||||
|                     return true; | ||||
|                   }, | ||||
|                   child: ScrollViewWithHeight( | ||||
|                     child: Hero( | ||||
|                       tag: "card", | ||||
|                       child: Material( | ||||
|                         type: MaterialType.transparency, | ||||
|                         child: Container( | ||||
|                           decoration: const BoxDecoration( | ||||
|                             color: Colors.white, | ||||
|                             borderRadius: | ||||
|                               BorderRadius.vertical(top: Radius.circular(30)), | ||||
|                           ), | ||||
|                           padding: const EdgeInsets.symmetric(vertical: 20), | ||||
|                           margin: const EdgeInsets.only(top: 200), | ||||
|                           width: double.infinity, | ||||
|                           child: Column( | ||||
|                             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                             children: [ | ||||
|                               const HeaderWidget( | ||||
|                                 title: "Today's Events", | ||||
|                                 link: HeaderLink( | ||||
|                                   text: "View more", | ||||
|                                   href: EventsRoute() | ||||
|                                 ), | ||||
|                               ), | ||||
|                               Padding( | ||||
|                                 padding: const EdgeInsets.symmetric(horizontal: 20), | ||||
|                                 child: EventsList(), | ||||
|                               ), | ||||
|                               const HeaderWidget(title: "Food & Dining"), | ||||
|                               const RestaurantsList(), | ||||
|                               const HeaderWidget(title: "Transportation"), | ||||
|                               const Padding( | ||||
|                                 padding: EdgeInsets.symmetric(horizontal: 20), | ||||
|                                 child: TransportationCard(), | ||||
|                               ), | ||||
|                             ], | ||||
|                           ), | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                       return true; | ||||
|                     }, | ||||
|                     child: ClipRect(child: HomeContent( | ||||
|                       collapse: _collapse, | ||||
|                     )), | ||||
|                   ), | ||||
|                 ), | ||||
|               ], | ||||
|                 ], | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|   | ||||
							
								
								
									
										14
									
								
								lib/src/screens/home/state.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								lib/src/screens/home/state.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class HomePageState extends ChangeNotifier { | ||||
|   bool _showScrollMessage = false; | ||||
|  | ||||
|   bool get showScrollMessage { | ||||
|     return _showScrollMessage; | ||||
|   } | ||||
|  | ||||
|   set showScrollMessage(bool value) { | ||||
|     _showScrollMessage = value; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| } | ||||
| @@ -50,7 +50,7 @@ class _StudentIdScreenState extends State<StudentIdScreen> { | ||||
|           if (details.delta.dy > sensitivity) { | ||||
|             // Down Swipe | ||||
|           } else if (details.delta.dy < -sensitivity) { | ||||
|             context.router.navigate(const HomeRoute()); | ||||
|             context.router.navigate(HomeRoute()); | ||||
|           } | ||||
|         }, | ||||
|         child: Stack( | ||||
| @@ -61,19 +61,13 @@ class _StudentIdScreenState extends State<StudentIdScreen> { | ||||
|                 child: ListView( | ||||
|                   padding: const EdgeInsets.all(40), | ||||
|                   children: [ | ||||
|                     Hero( | ||||
|                       tag: "title", | ||||
|                       child: Material( | ||||
|                         type: MaterialType.transparency, | ||||
|                         child: Text( | ||||
|                           "Furman ID", | ||||
|                           style: furmanTextStyle(const TextStyle( | ||||
|                             color: Color(0xff26183d), | ||||
|                             fontSize: 36, | ||||
|                             fontWeight: FontWeight.w800 | ||||
|                           )), | ||||
|                         ), | ||||
|                       ), | ||||
|                     Text( | ||||
|                       "Furman ID", | ||||
|                       style: furmanTextStyle(const TextStyle( | ||||
|                         color: Color(0xff26183d), | ||||
|                         fontSize: 36, | ||||
|                         fontWeight: FontWeight.w800 | ||||
|                       )), | ||||
|                     ), | ||||
|                     const SizedBox(height: 30), | ||||
|                     Center( | ||||
| @@ -131,37 +125,6 @@ class _StudentIdScreenState extends State<StudentIdScreen> { | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|             Align( | ||||
|               alignment: Alignment.bottomCenter, | ||||
|               child: Hero( | ||||
|                 tag: "card", | ||||
|                 child: Material( | ||||
|                   type: MaterialType.transparency, | ||||
|                   child: Container( | ||||
|                     height: 75, | ||||
|                     width: double.infinity, | ||||
|                     decoration: const BoxDecoration( | ||||
|                       color: Colors.white, | ||||
|                       borderRadius: BorderRadius.vertical( | ||||
|                         top: Radius.circular(30), | ||||
|                       ), | ||||
|                     ), | ||||
|                     child: Align( | ||||
|                       alignment: Alignment.topCenter, | ||||
|                       child: Container( | ||||
|                         margin: const EdgeInsets.only(top: 10), | ||||
|                         width: 40, | ||||
|                         height: 5, | ||||
|                         decoration: BoxDecoration( | ||||
|                           color: Colors.grey[400], | ||||
|                           borderRadius: BorderRadius.circular(20), | ||||
|                         ) | ||||
|                       ), | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|   | ||||
							
								
								
									
										57
									
								
								lib/src/services/weather/weather_service.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								lib/src/services/weather/weather_service.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| import 'package:furman_now/secrets.dart'; | ||||
| import 'package:geolocator/geolocator.dart'; | ||||
| import 'package:weather/weather.dart'; | ||||
|  | ||||
| class WeatherService { | ||||
|   static final WeatherFactory _factory = WeatherFactory(Secrets.openWeatherMapKey); | ||||
|  | ||||
|   static Future<String> getWeatherMessage() async { | ||||
|     var position = await _determinePosition(); | ||||
|     var weather = await _factory.currentWeatherByLocation( | ||||
|       position.latitude, | ||||
|       position.longitude, | ||||
|     ); | ||||
|     return "It's ${weather.temperature?.fahrenheit?.round()}º with ${weather.weatherDescription}"; | ||||
|   } | ||||
|  | ||||
|   /// Determine the current position of the device. | ||||
|   /// | ||||
|   /// When the location services are not enabled or permissions | ||||
|   /// are denied the `Future` will return an error. | ||||
|   static Future<Position> _determinePosition() async { | ||||
|     bool serviceEnabled; | ||||
|     LocationPermission permission; | ||||
|  | ||||
|     // Test if location services are enabled. | ||||
|     serviceEnabled = await Geolocator.isLocationServiceEnabled(); | ||||
|     if (!serviceEnabled) { | ||||
|       // Location services are not enabled don't continue | ||||
|       // accessing the position and request users of the | ||||
|       // App to enable the location services. | ||||
|       return Future.error('Location services are disabled.'); | ||||
|     } | ||||
|  | ||||
|     permission = await Geolocator.checkPermission(); | ||||
|     if (permission == LocationPermission.denied) { | ||||
|       permission = await Geolocator.requestPermission(); | ||||
|       if (permission == LocationPermission.denied) { | ||||
|         // Permissions are denied, next time you could try | ||||
|         // requesting permissions again (this is also where | ||||
|         // Android's shouldShowRequestPermissionRationale | ||||
|         // returned true. According to Android guidelines | ||||
|         // your App should show an explanatory UI now. | ||||
|         return Future.error('Location permissions are denied'); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (permission == LocationPermission.deniedForever) { | ||||
|       // Permissions are denied forever, handle appropriately. | ||||
|       return Future.error( | ||||
|           'Location permissions are permanently denied, we cannot request permissions.'); | ||||
|     } | ||||
|  | ||||
|     // When we reach here, permissions are granted and we can | ||||
|     // continue accessing the position of the device. | ||||
|     return await Geolocator.getCurrentPosition(); | ||||
|   } | ||||
| } | ||||
| @@ -2,9 +2,11 @@ import 'package:flutter/material.dart'; | ||||
|  | ||||
| class ScrollViewWithHeight extends StatelessWidget { | ||||
|   final Widget child; | ||||
|   final ScrollController? controller; | ||||
|  | ||||
|   const ScrollViewWithHeight({ | ||||
|     required this.child, | ||||
|     this.controller, | ||||
|     Key? key, | ||||
|   }) : super(key: key); | ||||
|  | ||||
| @@ -12,6 +14,7 @@ class ScrollViewWithHeight extends StatelessWidget { | ||||
|   Widget build(BuildContext context) { | ||||
|     return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { | ||||
|       return SingleChildScrollView( | ||||
|         controller: controller, | ||||
|         physics: const BouncingScrollPhysics( | ||||
|           parent: AlwaysScrollableScrollPhysics() | ||||
|         ), | ||||
|   | ||||
							
								
								
									
										44
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -7,14 +7,21 @@ packages: | ||||
|       name: _fe_analyzer_shared | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "46.0.0" | ||||
|     version: "47.0.0" | ||||
|   analyzer: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: analyzer | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "4.6.0" | ||||
|     version: "4.7.0" | ||||
|   animated_stack_widget: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: animated_stack_widget | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.0.4" | ||||
|   args: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -265,6 +272,20 @@ packages: | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "4.1.4" | ||||
|   flutter_map_marker_cluster: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: flutter_map_marker_cluster | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.5.4" | ||||
|   flutter_map_marker_popup: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: flutter_map_marker_popup | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "3.2.0" | ||||
|   flutter_remix: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -297,7 +318,7 @@ packages: | ||||
|     source: hosted | ||||
|     version: "2.1.3" | ||||
|   geolocator: | ||||
|     dependency: transitive | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: geolocator | ||||
|       url: "https://pub.dartlang.org" | ||||
| @@ -533,7 +554,7 @@ packages: | ||||
|       name: path_provider_android | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.0.19" | ||||
|     version: "2.0.20" | ||||
|   path_provider_ios: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -568,7 +589,7 @@ packages: | ||||
|       name: path_provider_windows | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.1.2" | ||||
|     version: "2.1.3" | ||||
|   petitparser: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -833,6 +854,13 @@ packages: | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.0.1" | ||||
|   weather: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: weather | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.0.1" | ||||
|   web_socket_channel: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -846,14 +874,14 @@ packages: | ||||
|       name: webkit_inspection_protocol | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "1.1.0" | ||||
|     version: "1.2.0" | ||||
|   win32: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: win32 | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "2.7.0" | ||||
|     version: "3.0.0" | ||||
|   wkt_parser: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -867,7 +895,7 @@ packages: | ||||
|       name: xdg_directories | ||||
|       url: "https://pub.dartlang.org" | ||||
|     source: hosted | ||||
|     version: "0.2.0+1" | ||||
|     version: "0.2.0+2" | ||||
|   xml: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -50,6 +50,9 @@ dependencies: | ||||
|   salomon_bottom_bar: ^3.3.1 | ||||
|   flutter_remix: ^0.0.3 | ||||
|   provider: ^6.0.3 | ||||
|   weather: ^2.0.1 | ||||
|   geolocator: ^9.0.1 | ||||
|   flutter_map_marker_cluster: ^0.5.4 | ||||
|  | ||||
| dev_dependencies: | ||||
|   flutter_test: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user