From 1e5382e8f13020f9d5a1a3173b6854f270af15d5 Mon Sep 17 00:00:00 2001 From: Michael Thomas Date: Sun, 11 Sep 2022 12:39:48 -0400 Subject: [PATCH] Update pull down gesture to be much smoother, refactor home page routing, load weather from OpenWeatherMap --- ios/Runner/Info.plist | 2 + lib/src/routes/index.dart | 11 +- lib/src/routes/index.gr.dart | 16 +- lib/src/screens/home/content.dart | 107 +++++++++ lib/src/screens/home/home_header.dart | 92 ++++++++ lib/src/screens/home/index.dart | 204 ++++++------------ lib/src/screens/home/state.dart | 14 ++ lib/src/screens/student_id/index.dart | 53 +---- lib/src/services/weather/weather_service.dart | 57 +++++ lib/src/widgets/scroll_view_height.dart | 3 + pubspec.lock | 44 +++- pubspec.yaml | 3 + 12 files changed, 396 insertions(+), 210 deletions(-) create mode 100644 lib/src/screens/home/content.dart create mode 100644 lib/src/screens/home/home_header.dart create mode 100644 lib/src/screens/home/state.dart create mode 100644 lib/src/services/weather/weather_service.dart diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index c119125..e5b82d3 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -45,5 +45,7 @@ CADisableMinimumFrameDurationOnPhone + NSLocationWhenInUseUsageDescription + Your location will be used to provide weather and map data. diff --git a/lib/src/routes/index.dart b/lib/src/routes/index.dart index 4db6e64..024e500 100644 --- a/lib/src/routes/index.dart +++ b/lib/src/routes/index.dart @@ -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(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), diff --git a/lib/src/routes/index.gr.dart b/lib/src/routes/index.gr.dart index fff86f4..d1bc88a 100644 --- a/lib/src/routes/index.gr.dart +++ b/lib/src/routes/index.gr.dart @@ -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( - routeData: routeData, child: const _i2.HeroEmptyRouterPage()); + routeData: routeData, child: const _i2.HomeScreen()); }, MapRoute.name: (routeData) { return _i8.MaterialPageX( @@ -51,8 +51,8 @@ class AppRouter extends _i8.RootStackRouter { HomeRoute.name: (routeData) { return _i8.CustomPage( 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( 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 { } /// generated route for -/// [_i2.HeroEmptyRouterPage] +/// [_i2.HomeScreen] class HomePageRouter extends _i8.PageRouteInfo { const HomePageRouter({List<_i8.PageRouteInfo>? children}) : super(HomePageRouter.name, path: 'home', initialChildren: children); @@ -129,7 +129,7 @@ class InfoRoute extends _i8.PageRouteInfo { } /// generated route for -/// [_i6.HomeScreen] +/// [_i6.HomePageHeader] class HomeRoute extends _i8.PageRouteInfo { const HomeRoute() : super(HomeRoute.name, path: ''); diff --git a/lib/src/screens/home/content.dart b/lib/src/screens/home/content.dart new file mode 100644 index 0000000..5e478fa --- /dev/null +++ b/lib/src/screens/home/content.dart @@ -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 createState() => _HomeContentState(); +} + +class _HomeContentState extends State { + 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( + tween: widget.collapse + ? Tween(begin: 200, end: constraints.maxHeight - 75) + : Tween(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(), + ), + ], + ), + ), + ] + ), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/src/screens/home/home_header.dart b/lib/src/screens/home/home_header.dart new file mode 100644 index 0000000..c2960a9 --- /dev/null +++ b/lib/src/screens/home/home_header.dart @@ -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(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( + builder: (context, state, _) => AnimatedCrossFade( + crossFadeState: + state.showScrollMessage + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + duration: const Duration(milliseconds: 100), + firstChild: FutureBuilder( + 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 + )), + ), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/src/screens/home/index.dart b/lib/src/screens/home/index.dart index 27483ac..564c789 100644 --- a/lib/src/screens/home/index.dart +++ b/lib/src/screens/home/index.dart @@ -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 { - 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(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( - 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( + 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().showScrollMessage) { + context.read().showScrollMessage = true; + } + } else { + if (context.read().showScrollMessage) { + context.read().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().showScrollMessage) { + context.read().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, + )), ), - ), - ], + ], + ), ), ), ), diff --git a/lib/src/screens/home/state.dart b/lib/src/screens/home/state.dart new file mode 100644 index 0000000..5a28f3f --- /dev/null +++ b/lib/src/screens/home/state.dart @@ -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(); + } +} \ No newline at end of file diff --git a/lib/src/screens/student_id/index.dart b/lib/src/screens/student_id/index.dart index 7cd477e..766a226 100644 --- a/lib/src/screens/student_id/index.dart +++ b/lib/src/screens/student_id/index.dart @@ -50,7 +50,7 @@ class _StudentIdScreenState extends State { 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 { 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 { ), ), ), - 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), - ) - ), - ), - ), - ), - ), - ), ], ), ), diff --git a/lib/src/services/weather/weather_service.dart b/lib/src/services/weather/weather_service.dart new file mode 100644 index 0000000..307fa92 --- /dev/null +++ b/lib/src/services/weather/weather_service.dart @@ -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 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 _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(); + } +} \ No newline at end of file diff --git a/lib/src/widgets/scroll_view_height.dart b/lib/src/widgets/scroll_view_height.dart index 7b3a275..5758b66 100644 --- a/lib/src/widgets/scroll_view_height.dart +++ b/lib/src/widgets/scroll_view_height.dart @@ -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() ), diff --git a/pubspec.lock b/pubspec.lock index 2194208..346209a 100644 --- a/pubspec.lock +++ b/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: diff --git a/pubspec.yaml b/pubspec.yaml index 330dfd3..73235d4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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: