From c265c46c0aa1a1d019fbd0381b1cd762f438e0e0 Mon Sep 17 00:00:00 2001 From: thePeras Date: Wed, 8 Jan 2025 18:48:29 +0000 Subject: [PATCH 1/7] floating bottom navbar --- .../common_widgets/pages_layouts/general/general.dart | 8 ++++---- .../pages_layouts/general/widgets/top_navigation_bar.dart | 4 +++- .../common_widgets/pages_layouts/secondary/secondary.dart | 5 ++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/uni_app/lib/view/common_widgets/pages_layouts/general/general.dart b/packages/uni_app/lib/view/common_widgets/pages_layouts/general/general.dart index 2532139a9..72b22e451 100644 --- a/packages/uni_app/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/packages/uni_app/lib/view/common_widgets/pages_layouts/general/general.dart @@ -110,10 +110,10 @@ abstract class GeneralPageViewState extends State { return Scaffold( backgroundColor: Theme.of(context).colorScheme.surface, appBar: getTopNavbar(context), - - // TODO:(thePeras): This should move to floating action button in order to be transparent in background - // See https://stackoverflow.com/questions/72246152/how-to-use-floating-bottom-navigation-bar-in-flutter - bottomNavigationBar: const AppBottomNavbar(), + extendBody: true, + floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + floatingActionButton: const AppBottomNavbar(), + floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation, body: RefreshState( onRefresh: onRefresh, header: getHeader(context), diff --git a/packages/uni_app/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart b/packages/uni_app/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart index 0b066e8bc..adb07a074 100644 --- a/packages/uni_app/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart +++ b/packages/uni_app/lib/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart @@ -6,6 +6,7 @@ class AppTopNavbar extends StatelessWidget implements PreferredSizeWidget { this.rightButton, this.leftButton, this.centerTitle = false, + this.heightSize = const Size.fromHeight(kToolbarHeight), super.key, }); @@ -13,9 +14,10 @@ class AppTopNavbar extends StatelessWidget implements PreferredSizeWidget { final Widget? rightButton; final Widget? leftButton; final bool centerTitle; + final Size heightSize; @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); + Size get preferredSize => heightSize; @override AppBar build(BuildContext context) { diff --git a/packages/uni_app/lib/view/common_widgets/pages_layouts/secondary/secondary.dart b/packages/uni_app/lib/view/common_widgets/pages_layouts/secondary/secondary.dart index 22dd8fdd0..1248480fa 100644 --- a/packages/uni_app/lib/view/common_widgets/pages_layouts/secondary/secondary.dart +++ b/packages/uni_app/lib/view/common_widgets/pages_layouts/secondary/secondary.dart @@ -13,7 +13,10 @@ abstract class SecondaryPageViewState return Scaffold( backgroundColor: Theme.of(context).colorScheme.surface, appBar: getTopNavbar(context), - bottomNavigationBar: const AppBottomNavbar(), + extendBody: true, + floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + floatingActionButton: const AppBottomNavbar(), + floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation, body: RefreshState( onRefresh: onRefresh, header: getHeader(context), From 63c5998926051a28fc63ea4e3043046a3764e1e2 Mon Sep 17 00:00:00 2001 From: thePeras Date: Wed, 8 Jan 2025 18:49:27 +0000 Subject: [PATCH 2/7] full map page --- packages/uni_app/lib/main.dart | 3 +- .../lib/view/locations/widgets/map.dart | 5 +- packages/uni_app/lib/view/map/map.dart | 122 ++++++++++++++++++ .../lib/view/map/widgets/faculty_map.dart | 42 ++++++ .../map/widgets/floorless_marker_popup.dart | 65 ++++++++++ .../uni_app/lib/view/map/widgets/icons.dart | 30 +++++ .../uni_app/lib/view/map/widgets/map.dart | 121 +++++++++++++++++ .../uni_app/lib/view/map/widgets/marker.dart | 49 +++++++ .../lib/view/map/widgets/marker_popup.dart | 107 +++++++++++++++ 9 files changed, 542 insertions(+), 2 deletions(-) create mode 100644 packages/uni_app/lib/view/map/map.dart create mode 100644 packages/uni_app/lib/view/map/widgets/faculty_map.dart create mode 100644 packages/uni_app/lib/view/map/widgets/floorless_marker_popup.dart create mode 100644 packages/uni_app/lib/view/map/widgets/icons.dart create mode 100644 packages/uni_app/lib/view/map/widgets/map.dart create mode 100644 packages/uni_app/lib/view/map/widgets/marker.dart create mode 100644 packages/uni_app/lib/view/map/widgets/marker_popup.dart diff --git a/packages/uni_app/lib/main.dart b/packages/uni_app/lib/main.dart index 312f665d0..e87668ef9 100644 --- a/packages/uni_app/lib/main.dart +++ b/packages/uni_app/lib/main.dart @@ -43,6 +43,7 @@ import 'package:uni/view/library/library.dart'; import 'package:uni/view/locale_notifier.dart'; import 'package:uni/view/locations/locations.dart'; import 'package:uni/view/login/login.dart'; +import 'package:uni/view/map/map.dart'; import 'package:uni/view/profile/profile.dart'; import 'package:uni/view/restaurant/restaurant_page_view.dart'; import 'package:uni/view/schedule/schedule.dart'; @@ -320,7 +321,7 @@ class ApplicationState extends State { ), '/${NavigationItem.navMap.route}': PageTransition.makePageTransition( - page: const TransportsPageView(), + page: const MapPage(), settings: settings, ), '/${NavigationItem.navProfile.route}': diff --git a/packages/uni_app/lib/view/locations/widgets/map.dart b/packages/uni_app/lib/view/locations/widgets/map.dart index 0e0e2ef3f..de5575a23 100644 --- a/packages/uni_app/lib/view/locations/widgets/map.dart +++ b/packages/uni_app/lib/view/locations/widgets/map.dart @@ -79,7 +79,10 @@ class LocationsMap extends StatelessWidget { ], children: [ TileLayer( - urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + // TODO(thePeras): Change layer in dark theme + //urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + urlTemplate: + 'https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png', subdomains: const ['a', 'b', 'c'], tileProvider: CachedTileProvider(), ), diff --git a/packages/uni_app/lib/view/map/map.dart b/packages/uni_app/lib/view/map/map.dart new file mode 100644 index 000000000..2a560f17e --- /dev/null +++ b/packages/uni_app/lib/view/map/map.dart @@ -0,0 +1,122 @@ +import 'package:diacritic/diacritic.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/model/entities/location_group.dart'; +import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; +import 'package:uni/utils/navigation_items.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; +import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart'; +import 'package:uni/view/lazy_consumer.dart'; +import 'package:uni/view/locations/widgets/faculty_map.dart'; + +class MapPage extends StatefulWidget { + const MapPage({super.key}); + + @override + MapPageState createState() => MapPageState(); +} + +class MapPageState extends GeneralPageViewState { + ScrollController? scrollViewController; + + @override + Widget getBody(BuildContext context) { + return LazyConsumer>( + builder: (context, locations) { + return MapPageView( + locations: locations, + ); + }, + hasContent: (locations) => locations.isNotEmpty, + onNullContent: Center(child: Text(S.of(context).no_places_info)), + ); + } + + @override + Future onRefresh(BuildContext context) async {} + + @override + String? getTitle() => + S.of(context).nav_title(NavigationItem.navLocations.route); + + @override + AppTopNavbar? getTopNavbar(BuildContext context) { + return const AppTopNavbar( + heightSize: Size.fromHeight(0), + ); + } +} + +class MapPageView extends StatefulWidget { + const MapPageView({ + required this.locations, + super.key, + }); + + final List locations; + + @override + MapPageViewState createState() => MapPageViewState(); +} + +class MapPageViewState extends State { + static GlobalKey searchFormKey = GlobalKey(); + static String searchTerms = ''; + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, constraints) { + return Column( + children: [ + Container( + width: constraints.maxWidth - 40, + height: 40, + child: TextFormField( + key: searchFormKey, + onChanged: (text) { + setState(() { + searchTerms = removeDiacritics(text.trim().toLowerCase()); + }); + }, + decoration: InputDecoration( + filled: true, + fillColor: Theme.of(context).colorScheme.secondary, + prefixIcon: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: SvgPicture.asset( + 'assets/images/logo_dark.svg', + semanticsLabel: 'search', + width: 10, + ), + ), + contentPadding: const EdgeInsets.all(10), + hintText: '${S.of(context).search}...', + ), + ), + ), + const SizedBox(height: 10), + Expanded( + child: FacultyMap( + faculty: getLocation(), + locations: widget.locations, + searchFilter: searchTerms, + interactiveFlags: InteractiveFlag.all - InteractiveFlag.rotate, + // TODO(bdmendes): add support for multiple faculties + ), + ), + const SizedBox( + height: 20, + ), + ], + ); + }, + ); + } + + String getLocation() { + return 'FEUP'; + } +} diff --git a/packages/uni_app/lib/view/map/widgets/faculty_map.dart b/packages/uni_app/lib/view/map/widgets/faculty_map.dart new file mode 100644 index 000000000..72364f8eb --- /dev/null +++ b/packages/uni_app/lib/view/map/widgets/faculty_map.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:uni/model/entities/location_group.dart'; +import 'package:uni/view/locations/widgets/map.dart'; + +class FacultyMap extends StatelessWidget { + const FacultyMap({ + required this.faculty, + required this.locations, + required this.searchFilter, + required this.interactiveFlags, + super.key, + }); + + final String faculty; + final List locations; + final String searchFilter; + final int interactiveFlags; + + @override + Widget build(BuildContext context) { + switch (faculty) { + case 'FEUP': + return LocationsMap( + northEastBoundary: const LatLng(41.17986, -8.59298), + southWestBoundary: const LatLng(41.17670, -8.59991), + center: const LatLng(41.17731, -8.59522), + locations: locations, + interactiveFlags: interactiveFlags, + searchFilter: searchFilter, + ); + default: + return Container(); // Should not happen + } + } + + static Color getFontColor(BuildContext context) { + return Theme.of(context).brightness == Brightness.light + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.tertiary; + } +} diff --git a/packages/uni_app/lib/view/map/widgets/floorless_marker_popup.dart b/packages/uni_app/lib/view/map/widgets/floorless_marker_popup.dart new file mode 100644 index 000000000..5ddd0b14c --- /dev/null +++ b/packages/uni_app/lib/view/map/widgets/floorless_marker_popup.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'package:uni/model/entities/location.dart'; +import 'package:uni/model/entities/location_group.dart'; +import 'package:uni/view/locations/widgets/faculty_map.dart'; + +class FloorlessLocationMarkerPopup extends StatelessWidget { + const FloorlessLocationMarkerPopup( + this.locationGroup, { + this.showId = false, + super.key, + }); + + final LocationGroup locationGroup; + final bool showId; + + @override + Widget build(BuildContext context) { + final locations = locationGroup.floors.values.expand((x) => x).toList(); + return Card( + color: Theme.of(context).colorScheme.surface.withOpacity(0.8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + child: Padding( + padding: const EdgeInsets.all(12), + child: Wrap( + direction: Axis.vertical, + spacing: 8, + children: (showId + ? [Text(locationGroup.id.toString())] + : []) + + locations + .map((location) => LocationRow(location: location)) + .toList(), + ), + ), + ); + } + + List buildLocations(BuildContext context, List locations) { + return locations + .map( + (location) => Text( + location.description(), + textAlign: TextAlign.left, + style: TextStyle(color: FacultyMap.getFontColor(context)), + ), + ) + .toList(); + } +} + +class LocationRow extends StatelessWidget { + const LocationRow({required this.location, super.key}); + final Location location; + + @override + Widget build(BuildContext context) { + return Text( + location.description(), + textAlign: TextAlign.left, + style: TextStyle(color: FacultyMap.getFontColor(context)), + ); + } +} diff --git a/packages/uni_app/lib/view/map/widgets/icons.dart b/packages/uni_app/lib/view/map/widgets/icons.dart new file mode 100644 index 000000000..eea463670 --- /dev/null +++ b/packages/uni_app/lib/view/map/widgets/icons.dart @@ -0,0 +1,30 @@ +import 'package:flutter/widgets.dart'; + +/// Flutter icons LocationIcons +/// Copyright (C) 2022 by original authors @ fluttericon.com, fontello.com +/// This font was generated by FlutterIcon.com, which is derived from Fontello. +/// +/// To use this font, place it in your fonts/ directory and include the +/// following in your pubspec.yaml +/// +/// flutter: +/// fonts: +/// - family: LocationIcons +/// fonts: +/// - asset: fonts/LocationIcons.ttf +/// +/// +/// +class LocationIcons { + LocationIcons._(); + + static const _kFontFam = 'LocationIcons'; + + static const IconData bookOpenBlankVariant = + IconData(0xe800, fontFamily: _kFontFam); + static const IconData bottleSodaClassic = + IconData(0xe801, fontFamily: _kFontFam); + static const IconData cashMultiple = IconData(0xe802, fontFamily: _kFontFam); + static const IconData coffee = IconData(0xe803, fontFamily: _kFontFam); + static const IconData printer = IconData(0xe804, fontFamily: _kFontFam); +} diff --git a/packages/uni_app/lib/view/map/widgets/map.dart b/packages/uni_app/lib/view/map/widgets/map.dart new file mode 100644 index 000000000..0e0e2ef3f --- /dev/null +++ b/packages/uni_app/lib/view/map/widgets/map.dart @@ -0,0 +1,121 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:diacritic/diacritic.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map_marker_popup/flutter_map_marker_popup.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:uni/controller/networking/url_launcher.dart'; +import 'package:uni/model/entities/location_group.dart'; +import 'package:uni/view/locations/widgets/floorless_marker_popup.dart'; +import 'package:uni/view/locations/widgets/marker.dart'; +import 'package:uni/view/locations/widgets/marker_popup.dart'; + +class LocationsMap extends StatelessWidget { + LocationsMap({ + required this.northEastBoundary, + required this.southWestBoundary, + required this.center, + required this.locations, + required this.interactiveFlags, + this.searchFilter = '', + super.key, + }); + + final PopupController _popupLayerController = PopupController(); + final List locations; + final LatLng northEastBoundary; + final LatLng southWestBoundary; + final LatLng center; + final int interactiveFlags; + + final String searchFilter; + + @override + Widget build(BuildContext context) { + final filteredLocations = List.from(locations); + if (searchFilter.trim().isNotEmpty) { + filteredLocations.retainWhere((location) { + final allLocations = location.floors.values.expand((x) => x); + return allLocations.any((location) { + return removeDiacritics(location.description().toLowerCase().trim()) + .contains( + searchFilter, + ); + }); + }); + } + + return FlutterMap( + options: MapOptions( + minZoom: 17, + maxZoom: 18, + nePanBoundary: northEastBoundary, + swPanBoundary: southWestBoundary, + center: center, + zoom: 17.5, + interactiveFlags: interactiveFlags, + onTap: (tapPosition, latlng) => _popupLayerController.hideAllPopups(), + ), + nonRotatedChildren: [ + Align( + alignment: Alignment.bottomRight, + child: ColoredBox( + color: Theme.of(context).colorScheme.onPrimary.withOpacity(0.8), + child: GestureDetector( + onTap: () => launchUrlWithToast( + context, + 'https://www.openstreetmap.org/copyright', + ), + child: const Padding( + padding: EdgeInsets.symmetric(vertical: 5, horizontal: 8), + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: Text('© OpenStreetMap'), + ), + ), + ), + ), + ), + ], + children: [ + TileLayer( + urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + subdomains: const ['a', 'b', 'c'], + tileProvider: CachedTileProvider(), + ), + PopupMarkerLayer( + options: PopupMarkerLayerOptions( + markers: filteredLocations.map((location) { + return LocationMarker(location.latlng, location); + }).toList(), + popupController: _popupLayerController, + popupDisplayOptions: PopupDisplayOptions( + animation: const PopupAnimation.fade( + duration: Duration(milliseconds: 400), + ), + builder: (_, marker) { + if (marker is LocationMarker) { + return marker.locationGroup.isFloorless + ? FloorlessLocationMarkerPopup(marker.locationGroup) + : LocationMarkerPopup(marker.locationGroup); + } + return const Card(child: Text('')); + }, + ), + ), + ), + ], + ); + } +} + +class CachedTileProvider extends TileProvider { + CachedTileProvider(); + + @override + ImageProvider getImage(TileCoordinates coordinates, TileLayer options) { + return CachedNetworkImageProvider( + getTileUrl(coordinates, options), + ); + } +} diff --git a/packages/uni_app/lib/view/map/widgets/marker.dart b/packages/uni_app/lib/view/map/widgets/marker.dart new file mode 100644 index 000000000..e1638306d --- /dev/null +++ b/packages/uni_app/lib/view/map/widgets/marker.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:uni/model/entities/location.dart'; +import 'package:uni/model/entities/location_group.dart'; +import 'package:uni/view/locations/widgets/faculty_map.dart'; + +class LocationMarker extends Marker { + LocationMarker(this.latlng, this.locationGroup) + : super( + anchorPos: AnchorPos.align(AnchorAlign.center), + height: 20, + width: 20, + point: latlng, + builder: (context) => DecoratedBox( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + border: Border.all( + color: Theme.of(context).colorScheme.primary, + ), + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + child: MarkerIcon( + location: locationGroup.getLocationWithMostWeight(), + ), + ), + ); + final LocationGroup locationGroup; + final LatLng latlng; +} + +class MarkerIcon extends StatelessWidget { + const MarkerIcon({super.key, this.location}); + final Location? location; + + @override + Widget build(BuildContext context) { + if (location == null) { + return Container(); + } + + final fontColor = FacultyMap.getFontColor(context); + if (location?.icon is IconData) { + return Icon(location?.icon as IconData, color: fontColor, size: 12); + } else { + return Icon(Icons.device_unknown, color: fontColor, size: 12); + } + } +} diff --git a/packages/uni_app/lib/view/map/widgets/marker_popup.dart b/packages/uni_app/lib/view/map/widgets/marker_popup.dart new file mode 100644 index 000000000..c9450c2c0 --- /dev/null +++ b/packages/uni_app/lib/view/map/widgets/marker_popup.dart @@ -0,0 +1,107 @@ +import 'package:flutter/material.dart'; +import 'package:uni/generated/l10n.dart'; +import 'package:uni/model/entities/location.dart'; +import 'package:uni/model/entities/location_group.dart'; +import 'package:uni/view/locations/widgets/faculty_map.dart'; + +class LocationMarkerPopup extends StatelessWidget { + const LocationMarkerPopup( + this.locationGroup, { + this.showId = false, + super.key, + }); + + final LocationGroup locationGroup; + final bool showId; + + @override + Widget build(BuildContext context) { + return Card( + color: Theme.of(context).colorScheme.surface.withOpacity(0.8), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(15), + ), + child: Padding( + padding: const EdgeInsets.all(12), + child: Wrap( + clipBehavior: Clip.antiAlias, + direction: Axis.vertical, + spacing: 8, + children: (showId + ? [Text(locationGroup.id.toString())] + : []) + + getEntries() + .map( + (entry) => Floor(floor: entry.key, locations: entry.value), + ) + .toList(), + ), + ), + ); + } + + List>> getEntries() { + return locationGroup.floors.entries.toList() + ..sort((current, next) => -current.key.compareTo(next.key)); + } +} + +class Floor extends StatelessWidget { + const Floor({required this.locations, required this.floor, super.key}); + + final List locations; + final int floor; + + @override + Widget build(BuildContext context) { + final fontColor = FacultyMap.getFontColor(context); + + final floorString = 0 <= floor && floor <= 9 // To maintain layout of popup + ? ' $floor' + : '$floor'; + + return Row( + children: [ + Container( + padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), + child: Text( + '${S.of(context).floor} $floorString', + style: TextStyle(color: fontColor), + ), + ), + Container( + padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), + decoration: + BoxDecoration(border: Border(left: BorderSide(color: fontColor))), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: locations + .map( + (location) => + LocationRow(location: location, color: fontColor), + ) + .toList(), + ), + ), + ], + ); + } +} + +class LocationRow extends StatelessWidget { + const LocationRow({required this.location, required this.color, super.key}); + + final Location location; + final Color color; + + @override + Widget build(BuildContext context) { + return Text( + location.description(), + textAlign: TextAlign.left, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: color), + ); + } +} From 9a292ab9bc9ec26308b27615d534f8549e778425 Mon Sep 17 00:00:00 2001 From: thePeras Date: Wed, 8 Jan 2025 18:53:26 +0000 Subject: [PATCH 3/7] map search input --- packages/uni_app/lib/view/locations/locations.dart | 1 + packages/uni_app/lib/view/map/map.dart | 9 ++++++--- packages/uni_app/lib/view/transports/transports.dart | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/uni_app/lib/view/locations/locations.dart b/packages/uni_app/lib/view/locations/locations.dart index 0d8dd55b7..7e339b16f 100644 --- a/packages/uni_app/lib/view/locations/locations.dart +++ b/packages/uni_app/lib/view/locations/locations.dart @@ -9,6 +9,7 @@ import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; import 'package:uni/view/lazy_consumer.dart'; import 'package:uni/view/locations/widgets/faculty_map.dart'; +// TODO(thePeras): To remove full folder class LocationsPage extends StatefulWidget { const LocationsPage({super.key}); diff --git a/packages/uni_app/lib/view/map/map.dart b/packages/uni_app/lib/view/map/map.dart index 2a560f17e..40c1101d6 100644 --- a/packages/uni_app/lib/view/map/map.dart +++ b/packages/uni_app/lib/view/map/map.dart @@ -71,9 +71,8 @@ class MapPageViewState extends State { builder: (context, constraints) { return Column( children: [ - Container( - width: constraints.maxWidth - 40, - height: 40, + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), child: TextFormField( key: searchFormKey, onChanged: (text) { @@ -92,6 +91,10 @@ class MapPageViewState extends State { width: 10, ), ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide.none, + ), contentPadding: const EdgeInsets.all(10), hintText: '${S.of(context).search}...', ), diff --git a/packages/uni_app/lib/view/transports/transports.dart b/packages/uni_app/lib/view/transports/transports.dart index eeb5b26ee..4f719dae1 100644 --- a/packages/uni_app/lib/view/transports/transports.dart +++ b/packages/uni_app/lib/view/transports/transports.dart @@ -6,6 +6,7 @@ import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/home/widgets/bus_stop_card.dart'; import 'package:uni/view/transports/widgets/map_snapshot_card.dart'; +// TODO(thePeras): Remove full folder class TransportsPageView extends StatefulWidget { const TransportsPageView({super.key}); From bb3a6281bd867e886ecbe6962bbe3fae62fc93b3 Mon Sep 17 00:00:00 2001 From: thePeras Date: Wed, 8 Jan 2025 19:18:11 +0000 Subject: [PATCH 4/7] map floating search input --- packages/uni_app/lib/main.dart | 11 +- .../uni_app/lib/view/locations/locations.dart | 116 ---------------- .../view/locations/widgets/faculty_map.dart | 42 ------ .../widgets/floorless_marker_popup.dart | 65 --------- .../lib/view/locations/widgets/icons.dart | 30 ----- .../lib/view/locations/widgets/map.dart | 124 ------------------ .../lib/view/locations/widgets/marker.dart | 49 ------- .../view/locations/widgets/marker_popup.dart | 107 --------------- packages/uni_app/lib/view/map/map.dart | 124 +++++++++++++++--- .../map/widgets/cached_tile_provider.dart | 14 ++ .../lib/view/map/widgets/faculty_map.dart | 42 ------ .../map/widgets/floorless_marker_popup.dart | 19 ++- .../uni_app/lib/view/map/widgets/map.dart | 121 ----------------- .../uni_app/lib/view/map/widgets/marker.dart | 10 +- .../lib/view/map/widgets/marker_popup.dart | 10 +- .../lib/view/transports/transports.dart | 40 ------ .../transports/widgets/map_snapshot_card.dart | 66 ---------- 17 files changed, 153 insertions(+), 837 deletions(-) delete mode 100644 packages/uni_app/lib/view/locations/locations.dart delete mode 100644 packages/uni_app/lib/view/locations/widgets/faculty_map.dart delete mode 100644 packages/uni_app/lib/view/locations/widgets/floorless_marker_popup.dart delete mode 100644 packages/uni_app/lib/view/locations/widgets/icons.dart delete mode 100644 packages/uni_app/lib/view/locations/widgets/map.dart delete mode 100644 packages/uni_app/lib/view/locations/widgets/marker.dart delete mode 100644 packages/uni_app/lib/view/locations/widgets/marker_popup.dart create mode 100644 packages/uni_app/lib/view/map/widgets/cached_tile_provider.dart delete mode 100644 packages/uni_app/lib/view/map/widgets/faculty_map.dart delete mode 100644 packages/uni_app/lib/view/map/widgets/map.dart delete mode 100644 packages/uni_app/lib/view/transports/transports.dart delete mode 100644 packages/uni_app/lib/view/transports/widgets/map_snapshot_card.dart diff --git a/packages/uni_app/lib/main.dart b/packages/uni_app/lib/main.dart index e87668ef9..58141df27 100644 --- a/packages/uni_app/lib/main.dart +++ b/packages/uni_app/lib/main.dart @@ -41,7 +41,6 @@ import 'package:uni/view/faculty/faculty.dart'; import 'package:uni/view/home/home.dart'; import 'package:uni/view/library/library.dart'; import 'package:uni/view/locale_notifier.dart'; -import 'package:uni/view/locations/locations.dart'; import 'package:uni/view/login/login.dart'; import 'package:uni/view/map/map.dart'; import 'package:uni/view/profile/profile.dart'; @@ -51,7 +50,6 @@ import 'package:uni/view/settings/settings.dart'; import 'package:uni/view/splash/splash.dart'; import 'package:uni/view/theme.dart'; import 'package:uni/view/theme_notifier.dart'; -import 'package:uni/view/transports/transports.dart'; import 'package:uni_ui/theme.dart'; import 'package:upgrader/upgrader.dart'; import 'package:workmanager/workmanager.dart'; @@ -289,9 +287,9 @@ class ApplicationState extends State { page: const CourseUnitsPageView(), settings: settings, ), - '/${NavigationItem.navLocations.route}': + '/${NavigationItem.navMap.route}': PageTransition.makePageTransition( - page: const LocationsPage(), + page: const MapPage(), settings: settings, ), '/${NavigationItem.navRestaurants.route}': @@ -319,11 +317,6 @@ class ApplicationState extends State { page: const AcademicPathPageView(), settings: settings, ), - '/${NavigationItem.navMap.route}': - PageTransition.makePageTransition( - page: const MapPage(), - settings: settings, - ), '/${NavigationItem.navProfile.route}': MaterialPageRoute( builder: (__) => const ProfilePageView(), diff --git a/packages/uni_app/lib/view/locations/locations.dart b/packages/uni_app/lib/view/locations/locations.dart deleted file mode 100644 index 7e339b16f..000000000 --- a/packages/uni_app/lib/view/locations/locations.dart +++ /dev/null @@ -1,116 +0,0 @@ -import 'package:diacritic/diacritic.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; -import 'package:uni/utils/navigation_items.dart'; -import 'package:uni/view/common_widgets/pages_layouts/secondary/secondary.dart'; -import 'package:uni/view/lazy_consumer.dart'; -import 'package:uni/view/locations/widgets/faculty_map.dart'; - -// TODO(thePeras): To remove full folder -class LocationsPage extends StatefulWidget { - const LocationsPage({super.key}); - - @override - LocationsPageState createState() => LocationsPageState(); -} - -class LocationsPageState extends SecondaryPageViewState - with SingleTickerProviderStateMixin { - ScrollController? scrollViewController; - - @override - Widget getBody(BuildContext context) { - return LazyConsumer>( - builder: (context, locations) { - return LocationsPageView( - locations: locations, - ); - }, - hasContent: (locations) => locations.isNotEmpty, - onNullContent: Center(child: Text(S.of(context).no_places_info)), - ); - } - - @override - Future onRefresh(BuildContext context) async {} - - @override - String? getTitle() => - S.of(context).nav_title(NavigationItem.navLocations.route); -} - -class LocationsPageView extends StatefulWidget { - const LocationsPageView({ - required this.locations, - super.key, - }); - - final List locations; - - @override - LocationsPageViewState createState() => LocationsPageViewState(); -} - -class LocationsPageViewState extends State { - static GlobalKey searchFormKey = GlobalKey(); - static String searchTerms = ''; - - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - return Column( - children: [ - Container( - width: constraints.maxWidth - 40, - height: 40, - margin: const EdgeInsets.fromLTRB(20, 10, 20, 0), - child: TextFormField( - key: searchFormKey, - onChanged: (text) { - setState(() { - searchTerms = removeDiacritics(text.trim().toLowerCase()); - }); - }, - decoration: InputDecoration( - prefixIcon: const Icon(Icons.search), - contentPadding: const EdgeInsets.all(10), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(50), - ), - hintText: '${S.of(context).search}...', - ), - ), - ), - const SizedBox(height: 10), - Expanded( - child: Container( - height: 10, - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - alignment: Alignment.center, - child: FacultyMap( - faculty: getLocation(), - locations: widget.locations, - searchFilter: searchTerms, - interactiveFlags: - InteractiveFlag.all - InteractiveFlag.rotate, - // TODO(bdmendes): add support for multiple faculties - ), - ), - ), - const SizedBox( - height: 20, - ), - ], - ); - }, - ); - } - - String getLocation() { - return 'FEUP'; - } -} diff --git a/packages/uni_app/lib/view/locations/widgets/faculty_map.dart b/packages/uni_app/lib/view/locations/widgets/faculty_map.dart deleted file mode 100644 index 72364f8eb..000000000 --- a/packages/uni_app/lib/view/locations/widgets/faculty_map.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:latlong2/latlong.dart'; -import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/view/locations/widgets/map.dart'; - -class FacultyMap extends StatelessWidget { - const FacultyMap({ - required this.faculty, - required this.locations, - required this.searchFilter, - required this.interactiveFlags, - super.key, - }); - - final String faculty; - final List locations; - final String searchFilter; - final int interactiveFlags; - - @override - Widget build(BuildContext context) { - switch (faculty) { - case 'FEUP': - return LocationsMap( - northEastBoundary: const LatLng(41.17986, -8.59298), - southWestBoundary: const LatLng(41.17670, -8.59991), - center: const LatLng(41.17731, -8.59522), - locations: locations, - interactiveFlags: interactiveFlags, - searchFilter: searchFilter, - ); - default: - return Container(); // Should not happen - } - } - - static Color getFontColor(BuildContext context) { - return Theme.of(context).brightness == Brightness.light - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.tertiary; - } -} diff --git a/packages/uni_app/lib/view/locations/widgets/floorless_marker_popup.dart b/packages/uni_app/lib/view/locations/widgets/floorless_marker_popup.dart deleted file mode 100644 index 5ddd0b14c..000000000 --- a/packages/uni_app/lib/view/locations/widgets/floorless_marker_popup.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uni/model/entities/location.dart'; -import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/view/locations/widgets/faculty_map.dart'; - -class FloorlessLocationMarkerPopup extends StatelessWidget { - const FloorlessLocationMarkerPopup( - this.locationGroup, { - this.showId = false, - super.key, - }); - - final LocationGroup locationGroup; - final bool showId; - - @override - Widget build(BuildContext context) { - final locations = locationGroup.floors.values.expand((x) => x).toList(); - return Card( - color: Theme.of(context).colorScheme.surface.withOpacity(0.8), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15), - ), - child: Padding( - padding: const EdgeInsets.all(12), - child: Wrap( - direction: Axis.vertical, - spacing: 8, - children: (showId - ? [Text(locationGroup.id.toString())] - : []) + - locations - .map((location) => LocationRow(location: location)) - .toList(), - ), - ), - ); - } - - List buildLocations(BuildContext context, List locations) { - return locations - .map( - (location) => Text( - location.description(), - textAlign: TextAlign.left, - style: TextStyle(color: FacultyMap.getFontColor(context)), - ), - ) - .toList(); - } -} - -class LocationRow extends StatelessWidget { - const LocationRow({required this.location, super.key}); - final Location location; - - @override - Widget build(BuildContext context) { - return Text( - location.description(), - textAlign: TextAlign.left, - style: TextStyle(color: FacultyMap.getFontColor(context)), - ); - } -} diff --git a/packages/uni_app/lib/view/locations/widgets/icons.dart b/packages/uni_app/lib/view/locations/widgets/icons.dart deleted file mode 100644 index eea463670..000000000 --- a/packages/uni_app/lib/view/locations/widgets/icons.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/widgets.dart'; - -/// Flutter icons LocationIcons -/// Copyright (C) 2022 by original authors @ fluttericon.com, fontello.com -/// This font was generated by FlutterIcon.com, which is derived from Fontello. -/// -/// To use this font, place it in your fonts/ directory and include the -/// following in your pubspec.yaml -/// -/// flutter: -/// fonts: -/// - family: LocationIcons -/// fonts: -/// - asset: fonts/LocationIcons.ttf -/// -/// -/// -class LocationIcons { - LocationIcons._(); - - static const _kFontFam = 'LocationIcons'; - - static const IconData bookOpenBlankVariant = - IconData(0xe800, fontFamily: _kFontFam); - static const IconData bottleSodaClassic = - IconData(0xe801, fontFamily: _kFontFam); - static const IconData cashMultiple = IconData(0xe802, fontFamily: _kFontFam); - static const IconData coffee = IconData(0xe803, fontFamily: _kFontFam); - static const IconData printer = IconData(0xe804, fontFamily: _kFontFam); -} diff --git a/packages/uni_app/lib/view/locations/widgets/map.dart b/packages/uni_app/lib/view/locations/widgets/map.dart deleted file mode 100644 index de5575a23..000000000 --- a/packages/uni_app/lib/view/locations/widgets/map.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:diacritic/diacritic.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map_marker_popup/flutter_map_marker_popup.dart'; -import 'package:latlong2/latlong.dart'; -import 'package:uni/controller/networking/url_launcher.dart'; -import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/view/locations/widgets/floorless_marker_popup.dart'; -import 'package:uni/view/locations/widgets/marker.dart'; -import 'package:uni/view/locations/widgets/marker_popup.dart'; - -class LocationsMap extends StatelessWidget { - LocationsMap({ - required this.northEastBoundary, - required this.southWestBoundary, - required this.center, - required this.locations, - required this.interactiveFlags, - this.searchFilter = '', - super.key, - }); - - final PopupController _popupLayerController = PopupController(); - final List locations; - final LatLng northEastBoundary; - final LatLng southWestBoundary; - final LatLng center; - final int interactiveFlags; - - final String searchFilter; - - @override - Widget build(BuildContext context) { - final filteredLocations = List.from(locations); - if (searchFilter.trim().isNotEmpty) { - filteredLocations.retainWhere((location) { - final allLocations = location.floors.values.expand((x) => x); - return allLocations.any((location) { - return removeDiacritics(location.description().toLowerCase().trim()) - .contains( - searchFilter, - ); - }); - }); - } - - return FlutterMap( - options: MapOptions( - minZoom: 17, - maxZoom: 18, - nePanBoundary: northEastBoundary, - swPanBoundary: southWestBoundary, - center: center, - zoom: 17.5, - interactiveFlags: interactiveFlags, - onTap: (tapPosition, latlng) => _popupLayerController.hideAllPopups(), - ), - nonRotatedChildren: [ - Align( - alignment: Alignment.bottomRight, - child: ColoredBox( - color: Theme.of(context).colorScheme.onPrimary.withOpacity(0.8), - child: GestureDetector( - onTap: () => launchUrlWithToast( - context, - 'https://www.openstreetmap.org/copyright', - ), - child: const Padding( - padding: EdgeInsets.symmetric(vertical: 5, horizontal: 8), - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: Text('© OpenStreetMap'), - ), - ), - ), - ), - ), - ], - children: [ - TileLayer( - // TODO(thePeras): Change layer in dark theme - //urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', - urlTemplate: - 'https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png', - subdomains: const ['a', 'b', 'c'], - tileProvider: CachedTileProvider(), - ), - PopupMarkerLayer( - options: PopupMarkerLayerOptions( - markers: filteredLocations.map((location) { - return LocationMarker(location.latlng, location); - }).toList(), - popupController: _popupLayerController, - popupDisplayOptions: PopupDisplayOptions( - animation: const PopupAnimation.fade( - duration: Duration(milliseconds: 400), - ), - builder: (_, marker) { - if (marker is LocationMarker) { - return marker.locationGroup.isFloorless - ? FloorlessLocationMarkerPopup(marker.locationGroup) - : LocationMarkerPopup(marker.locationGroup); - } - return const Card(child: Text('')); - }, - ), - ), - ), - ], - ); - } -} - -class CachedTileProvider extends TileProvider { - CachedTileProvider(); - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) { - return CachedNetworkImageProvider( - getTileUrl(coordinates, options), - ); - } -} diff --git a/packages/uni_app/lib/view/locations/widgets/marker.dart b/packages/uni_app/lib/view/locations/widgets/marker.dart deleted file mode 100644 index e1638306d..000000000 --- a/packages/uni_app/lib/view/locations/widgets/marker.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:latlong2/latlong.dart'; -import 'package:uni/model/entities/location.dart'; -import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/view/locations/widgets/faculty_map.dart'; - -class LocationMarker extends Marker { - LocationMarker(this.latlng, this.locationGroup) - : super( - anchorPos: AnchorPos.align(AnchorAlign.center), - height: 20, - width: 20, - point: latlng, - builder: (context) => DecoratedBox( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - border: Border.all( - color: Theme.of(context).colorScheme.primary, - ), - borderRadius: const BorderRadius.all(Radius.circular(20)), - ), - child: MarkerIcon( - location: locationGroup.getLocationWithMostWeight(), - ), - ), - ); - final LocationGroup locationGroup; - final LatLng latlng; -} - -class MarkerIcon extends StatelessWidget { - const MarkerIcon({super.key, this.location}); - final Location? location; - - @override - Widget build(BuildContext context) { - if (location == null) { - return Container(); - } - - final fontColor = FacultyMap.getFontColor(context); - if (location?.icon is IconData) { - return Icon(location?.icon as IconData, color: fontColor, size: 12); - } else { - return Icon(Icons.device_unknown, color: fontColor, size: 12); - } - } -} diff --git a/packages/uni_app/lib/view/locations/widgets/marker_popup.dart b/packages/uni_app/lib/view/locations/widgets/marker_popup.dart deleted file mode 100644 index c9450c2c0..000000000 --- a/packages/uni_app/lib/view/locations/widgets/marker_popup.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/model/entities/location.dart'; -import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/view/locations/widgets/faculty_map.dart'; - -class LocationMarkerPopup extends StatelessWidget { - const LocationMarkerPopup( - this.locationGroup, { - this.showId = false, - super.key, - }); - - final LocationGroup locationGroup; - final bool showId; - - @override - Widget build(BuildContext context) { - return Card( - color: Theme.of(context).colorScheme.surface.withOpacity(0.8), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(15), - ), - child: Padding( - padding: const EdgeInsets.all(12), - child: Wrap( - clipBehavior: Clip.antiAlias, - direction: Axis.vertical, - spacing: 8, - children: (showId - ? [Text(locationGroup.id.toString())] - : []) + - getEntries() - .map( - (entry) => Floor(floor: entry.key, locations: entry.value), - ) - .toList(), - ), - ), - ); - } - - List>> getEntries() { - return locationGroup.floors.entries.toList() - ..sort((current, next) => -current.key.compareTo(next.key)); - } -} - -class Floor extends StatelessWidget { - const Floor({required this.locations, required this.floor, super.key}); - - final List locations; - final int floor; - - @override - Widget build(BuildContext context) { - final fontColor = FacultyMap.getFontColor(context); - - final floorString = 0 <= floor && floor <= 9 // To maintain layout of popup - ? ' $floor' - : '$floor'; - - return Row( - children: [ - Container( - padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), - child: Text( - '${S.of(context).floor} $floorString', - style: TextStyle(color: fontColor), - ), - ), - Container( - padding: const EdgeInsets.fromLTRB(8, 0, 8, 0), - decoration: - BoxDecoration(border: Border(left: BorderSide(color: fontColor))), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: locations - .map( - (location) => - LocationRow(location: location, color: fontColor), - ) - .toList(), - ), - ), - ], - ); - } -} - -class LocationRow extends StatelessWidget { - const LocationRow({required this.location, required this.color, super.key}); - - final Location location; - final Color color; - - @override - Widget build(BuildContext context) { - return Text( - location.description(), - textAlign: TextAlign.left, - overflow: TextOverflow.ellipsis, - style: TextStyle(color: color), - ); - } -} diff --git a/packages/uni_app/lib/view/map/map.dart b/packages/uni_app/lib/view/map/map.dart index 40c1101d6..9a6ab35d8 100644 --- a/packages/uni_app/lib/view/map/map.dart +++ b/packages/uni_app/lib/view/map/map.dart @@ -1,7 +1,10 @@ import 'package:diacritic/diacritic.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; +import 'package:flutter_map_marker_popup/flutter_map_marker_popup.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:latlong2/latlong.dart'; +import 'package:uni/controller/networking/url_launcher.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/location_group.dart'; import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; @@ -9,7 +12,10 @@ import 'package:uni/utils/navigation_items.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; import 'package:uni/view/common_widgets/pages_layouts/general/widgets/top_navigation_bar.dart'; import 'package:uni/view/lazy_consumer.dart'; -import 'package:uni/view/locations/widgets/faculty_map.dart'; +import 'package:uni/view/map/widgets/cached_tile_provider.dart'; +import 'package:uni/view/map/widgets/floorless_marker_popup.dart'; +import 'package:uni/view/map/widgets/marker.dart'; +import 'package:uni/view/map/widgets/marker_popup.dart'; class MapPage extends StatefulWidget { const MapPage({super.key}); @@ -64,12 +70,98 @@ class MapPageView extends StatefulWidget { class MapPageViewState extends State { static GlobalKey searchFormKey = GlobalKey(); static String searchTerms = ''; + PopupController _popupLayerController = PopupController(); + + @override + void initState() { + super.initState(); + searchTerms = ''; + _popupLayerController = PopupController(); + } + + @override + void dispose() { + _popupLayerController.dispose(); + super.dispose(); + } @override Widget build(BuildContext context) { - return LayoutBuilder( - builder: (context, constraints) { - return Column( + final filteredLocations = List.from(widget.locations); + if (searchTerms.trim().isNotEmpty) { + filteredLocations.retainWhere((location) { + final allLocations = location.floors.values.expand((x) => x); + return allLocations.any((location) { + return removeDiacritics(location.description().toLowerCase().trim()) + .contains( + searchTerms, + ); + }); + }); + } + + return FlutterMap( + options: MapOptions( + minZoom: 17, + maxZoom: 18, + nePanBoundary: const LatLng(41.17986, -8.59298), + swPanBoundary: const LatLng(41.17670, -8.59991), + center: const LatLng(41.17731, -8.59522), + zoom: 17.5, + interactiveFlags: InteractiveFlag.all - InteractiveFlag.rotate, + onTap: (tapPosition, latlng) => _popupLayerController.hideAllPopups(), + ), + nonRotatedChildren: [ + Align( + alignment: Alignment.bottomRight, + child: ColoredBox( + color: Theme.of(context).colorScheme.onPrimary.withOpacity(0.8), + child: GestureDetector( + onTap: () => launchUrlWithToast( + context, + 'https://www.openstreetmap.org/copyright', + ), + child: const Padding( + padding: EdgeInsets.symmetric(vertical: 5, horizontal: 8), + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: Text('© OpenStreetMap'), + ), + ), + ), + ), + ), + ], + children: [ + TileLayer( + // TODO(thePeras): Ideia - change layer in dark theme + urlTemplate: + 'https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png', + subdomains: const ['a', 'b', 'c'], + tileProvider: CachedTileProvider(), + ), + PopupMarkerLayer( + options: PopupMarkerLayerOptions( + markers: filteredLocations.map((location) { + return LocationMarker(location.latlng, location); + }).toList(), + popupController: _popupLayerController, + popupDisplayOptions: PopupDisplayOptions( + animation: const PopupAnimation.fade( + duration: Duration(milliseconds: 400), + ), + builder: (_, marker) { + if (marker is LocationMarker) { + return marker.locationGroup.isFloorless + ? FloorlessLocationMarkerPopup(marker.locationGroup) + : LocationMarkerPopup(marker.locationGroup); + } + return const Card(child: Text('')); + }, + ), + ), + ), + Column( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 20), @@ -100,25 +192,19 @@ class MapPageViewState extends State { ), ), ), - const SizedBox(height: 10), - Expanded( - child: FacultyMap( - faculty: getLocation(), - locations: widget.locations, - searchFilter: searchTerms, - interactiveFlags: InteractiveFlag.all - InteractiveFlag.rotate, - // TODO(bdmendes): add support for multiple faculties - ), - ), - const SizedBox( - height: 20, - ), ], - ); - }, + ) + ], ); } + // TODO(thePeras): Weird + static Color getFontColor(BuildContext context) { + return Theme.of(context).brightness == Brightness.light + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.tertiary; + } + String getLocation() { return 'FEUP'; } diff --git a/packages/uni_app/lib/view/map/widgets/cached_tile_provider.dart b/packages/uni_app/lib/view/map/widgets/cached_tile_provider.dart new file mode 100644 index 000000000..43ac605b0 --- /dev/null +++ b/packages/uni_app/lib/view/map/widgets/cached_tile_provider.dart @@ -0,0 +1,14 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; + +class CachedTileProvider extends TileProvider { + CachedTileProvider(); + + @override + ImageProvider getImage(TileCoordinates coordinates, TileLayer options) { + return CachedNetworkImageProvider( + getTileUrl(coordinates, options), + ); + } +} diff --git a/packages/uni_app/lib/view/map/widgets/faculty_map.dart b/packages/uni_app/lib/view/map/widgets/faculty_map.dart deleted file mode 100644 index 72364f8eb..000000000 --- a/packages/uni_app/lib/view/map/widgets/faculty_map.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:latlong2/latlong.dart'; -import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/view/locations/widgets/map.dart'; - -class FacultyMap extends StatelessWidget { - const FacultyMap({ - required this.faculty, - required this.locations, - required this.searchFilter, - required this.interactiveFlags, - super.key, - }); - - final String faculty; - final List locations; - final String searchFilter; - final int interactiveFlags; - - @override - Widget build(BuildContext context) { - switch (faculty) { - case 'FEUP': - return LocationsMap( - northEastBoundary: const LatLng(41.17986, -8.59298), - southWestBoundary: const LatLng(41.17670, -8.59991), - center: const LatLng(41.17731, -8.59522), - locations: locations, - interactiveFlags: interactiveFlags, - searchFilter: searchFilter, - ); - default: - return Container(); // Should not happen - } - } - - static Color getFontColor(BuildContext context) { - return Theme.of(context).brightness == Brightness.light - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.tertiary; - } -} diff --git a/packages/uni_app/lib/view/map/widgets/floorless_marker_popup.dart b/packages/uni_app/lib/view/map/widgets/floorless_marker_popup.dart index 5ddd0b14c..5f325c6cb 100644 --- a/packages/uni_app/lib/view/map/widgets/floorless_marker_popup.dart +++ b/packages/uni_app/lib/view/map/widgets/floorless_marker_popup.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:uni/model/entities/location.dart'; import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/view/locations/widgets/faculty_map.dart'; class FloorlessLocationMarkerPopup extends StatelessWidget { const FloorlessLocationMarkerPopup( @@ -43,11 +42,18 @@ class FloorlessLocationMarkerPopup extends StatelessWidget { (location) => Text( location.description(), textAlign: TextAlign.left, - style: TextStyle(color: FacultyMap.getFontColor(context)), + style: TextStyle(color: _getFontColor(context)), ), ) .toList(); } + + // TODO(thePeras): Duplicated code + Color _getFontColor(BuildContext context) { + return Theme.of(context).brightness == Brightness.light + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.tertiary; + } } class LocationRow extends StatelessWidget { @@ -59,7 +65,14 @@ class LocationRow extends StatelessWidget { return Text( location.description(), textAlign: TextAlign.left, - style: TextStyle(color: FacultyMap.getFontColor(context)), + style: TextStyle(color: _getFontColor(context)), ); } + + // TODO(thePeras): Duplicated code + Color _getFontColor(BuildContext context) { + return Theme.of(context).brightness == Brightness.light + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.tertiary; + } } diff --git a/packages/uni_app/lib/view/map/widgets/map.dart b/packages/uni_app/lib/view/map/widgets/map.dart deleted file mode 100644 index 0e0e2ef3f..000000000 --- a/packages/uni_app/lib/view/map/widgets/map.dart +++ /dev/null @@ -1,121 +0,0 @@ -import 'package:cached_network_image/cached_network_image.dart'; -import 'package:diacritic/diacritic.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:flutter_map_marker_popup/flutter_map_marker_popup.dart'; -import 'package:latlong2/latlong.dart'; -import 'package:uni/controller/networking/url_launcher.dart'; -import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/view/locations/widgets/floorless_marker_popup.dart'; -import 'package:uni/view/locations/widgets/marker.dart'; -import 'package:uni/view/locations/widgets/marker_popup.dart'; - -class LocationsMap extends StatelessWidget { - LocationsMap({ - required this.northEastBoundary, - required this.southWestBoundary, - required this.center, - required this.locations, - required this.interactiveFlags, - this.searchFilter = '', - super.key, - }); - - final PopupController _popupLayerController = PopupController(); - final List locations; - final LatLng northEastBoundary; - final LatLng southWestBoundary; - final LatLng center; - final int interactiveFlags; - - final String searchFilter; - - @override - Widget build(BuildContext context) { - final filteredLocations = List.from(locations); - if (searchFilter.trim().isNotEmpty) { - filteredLocations.retainWhere((location) { - final allLocations = location.floors.values.expand((x) => x); - return allLocations.any((location) { - return removeDiacritics(location.description().toLowerCase().trim()) - .contains( - searchFilter, - ); - }); - }); - } - - return FlutterMap( - options: MapOptions( - minZoom: 17, - maxZoom: 18, - nePanBoundary: northEastBoundary, - swPanBoundary: southWestBoundary, - center: center, - zoom: 17.5, - interactiveFlags: interactiveFlags, - onTap: (tapPosition, latlng) => _popupLayerController.hideAllPopups(), - ), - nonRotatedChildren: [ - Align( - alignment: Alignment.bottomRight, - child: ColoredBox( - color: Theme.of(context).colorScheme.onPrimary.withOpacity(0.8), - child: GestureDetector( - onTap: () => launchUrlWithToast( - context, - 'https://www.openstreetmap.org/copyright', - ), - child: const Padding( - padding: EdgeInsets.symmetric(vertical: 5, horizontal: 8), - child: MouseRegion( - cursor: SystemMouseCursors.click, - child: Text('© OpenStreetMap'), - ), - ), - ), - ), - ), - ], - children: [ - TileLayer( - urlTemplate: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', - subdomains: const ['a', 'b', 'c'], - tileProvider: CachedTileProvider(), - ), - PopupMarkerLayer( - options: PopupMarkerLayerOptions( - markers: filteredLocations.map((location) { - return LocationMarker(location.latlng, location); - }).toList(), - popupController: _popupLayerController, - popupDisplayOptions: PopupDisplayOptions( - animation: const PopupAnimation.fade( - duration: Duration(milliseconds: 400), - ), - builder: (_, marker) { - if (marker is LocationMarker) { - return marker.locationGroup.isFloorless - ? FloorlessLocationMarkerPopup(marker.locationGroup) - : LocationMarkerPopup(marker.locationGroup); - } - return const Card(child: Text('')); - }, - ), - ), - ), - ], - ); - } -} - -class CachedTileProvider extends TileProvider { - CachedTileProvider(); - - @override - ImageProvider getImage(TileCoordinates coordinates, TileLayer options) { - return CachedNetworkImageProvider( - getTileUrl(coordinates, options), - ); - } -} diff --git a/packages/uni_app/lib/view/map/widgets/marker.dart b/packages/uni_app/lib/view/map/widgets/marker.dart index e1638306d..3afce2b1b 100644 --- a/packages/uni_app/lib/view/map/widgets/marker.dart +++ b/packages/uni_app/lib/view/map/widgets/marker.dart @@ -3,7 +3,6 @@ import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; import 'package:uni/model/entities/location.dart'; import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/view/locations/widgets/faculty_map.dart'; class LocationMarker extends Marker { LocationMarker(this.latlng, this.locationGroup) @@ -39,11 +38,18 @@ class MarkerIcon extends StatelessWidget { return Container(); } - final fontColor = FacultyMap.getFontColor(context); + final fontColor = _getFontColor(context); if (location?.icon is IconData) { return Icon(location?.icon as IconData, color: fontColor, size: 12); } else { return Icon(Icons.device_unknown, color: fontColor, size: 12); } } + + // TODO(thePeras): Duplicated code + Color _getFontColor(BuildContext context) { + return Theme.of(context).brightness == Brightness.light + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.tertiary; + } } diff --git a/packages/uni_app/lib/view/map/widgets/marker_popup.dart b/packages/uni_app/lib/view/map/widgets/marker_popup.dart index c9450c2c0..f4891e0ce 100644 --- a/packages/uni_app/lib/view/map/widgets/marker_popup.dart +++ b/packages/uni_app/lib/view/map/widgets/marker_popup.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/location.dart'; import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/view/locations/widgets/faculty_map.dart'; class LocationMarkerPopup extends StatelessWidget { const LocationMarkerPopup( @@ -54,7 +53,7 @@ class Floor extends StatelessWidget { @override Widget build(BuildContext context) { - final fontColor = FacultyMap.getFontColor(context); + final fontColor = _getFontColor(context); final floorString = 0 <= floor && floor <= 9 // To maintain layout of popup ? ' $floor' @@ -87,6 +86,13 @@ class Floor extends StatelessWidget { ], ); } + + // TODO(thePeras): Duplicated code + Color _getFontColor(BuildContext context) { + return Theme.of(context).brightness == Brightness.light + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.tertiary; + } } class LocationRow extends StatelessWidget { diff --git a/packages/uni_app/lib/view/transports/transports.dart b/packages/uni_app/lib/view/transports/transports.dart deleted file mode 100644 index 4f719dae1..000000000 --- a/packages/uni_app/lib/view/transports/transports.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/utils/navigation_items.dart'; -import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/common_widgets/pages_layouts/general/general.dart'; -import 'package:uni/view/home/widgets/bus_stop_card.dart'; -import 'package:uni/view/transports/widgets/map_snapshot_card.dart'; - -// TODO(thePeras): Remove full folder -class TransportsPageView extends StatefulWidget { - const TransportsPageView({super.key}); - - @override - State createState() => TransportsPageViewState(); -} - -class TransportsPageViewState extends GeneralPageViewState { - List transportsCards = [ - MapCard(), - BusStopCard(), - // Add more cards if needed - ]; - - @override - String? getTitle() => S.of(context).nav_title(NavigationItem.navMap.route); - - @override - Widget getBody(BuildContext context) { - return ListView( - children: transportsCards, - ); - } - - @override - Future onRefresh(BuildContext context) async { - for (final card in transportsCards) { - card.onRefresh(context); - } - } -} diff --git a/packages/uni_app/lib/view/transports/widgets/map_snapshot_card.dart b/packages/uni_app/lib/view/transports/widgets/map_snapshot_card.dart deleted file mode 100644 index 5ba79c1f5..000000000 --- a/packages/uni_app/lib/view/transports/widgets/map_snapshot_card.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:provider/provider.dart'; -import 'package:uni/generated/l10n.dart'; -import 'package:uni/model/entities/location_group.dart'; -import 'package:uni/model/providers/lazy/faculty_locations_provider.dart'; -import 'package:uni/utils/navigation_items.dart'; -import 'package:uni/view/common_widgets/generic_card.dart'; -import 'package:uni/view/lazy_consumer.dart'; -import 'package:uni/view/locations/widgets/faculty_map.dart'; - -class MapCard extends GenericCard { - MapCard({super.key}); - - const MapCard.fromEditingInformation( - super.key, { - required super.editingMode, - super.onDelete, - }) : super.fromEditingInformation(); - - @override - String getTitle(BuildContext context) => - '${S.of(context).nav_title(NavigationItem.navLocations.route)}: FEUP'; - - @override - Future onClick(BuildContext context) => - Navigator.pushNamed(context, '/${NavigationItem.navLocations.route}'); - - @override - Widget buildCardContent(BuildContext context) { - return LazyConsumer>( - builder: buildMapView, - hasContent: (locations) => locations.isNotEmpty, - onNullContent: const Center(child: Text('Erro')), - ); - } - - @override - void onRefresh(BuildContext context) { - Provider.of(context, listen: false) - .forceRefresh(context); - } - - Widget buildMapView(BuildContext context, List locations) { - return GestureDetector( - onTap: () => - Navigator.pushNamed(context, '/${NavigationItem.navLocations.route}'), - child: AbsorbPointer( - child: Container( - padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), - height: MediaQuery.of(context).size.height * 0.3, - alignment: Alignment.center, - child: ClipRRect( - borderRadius: BorderRadius.circular(10), - child: FacultyMap( - faculty: 'FEUP', - locations: locations, - interactiveFlags: InteractiveFlag.none, - searchFilter: '', - ), - ), - ), - ), - ); - } -} From 4ec23863d2b3f8f7c099f711cdbe82100c17de23 Mon Sep 17 00:00:00 2001 From: thePeras Date: Fri, 17 Jan 2025 12:28:05 +0000 Subject: [PATCH 5/7] update map icons style --- .../uni_app/assets/fonts/LocationIcons.ttf | Bin 2472 -> 0 bytes .../lib/model/entities/locations/atm.dart | 4 +-- .../entities/locations/coffee_machine.dart | 4 +-- .../lib/model/entities/locations/printer.dart | 4 +-- .../locations/restaurant_location.dart | 4 +-- .../locations/room_group_location.dart | 4 +-- .../entities/locations/room_location.dart | 4 +-- .../locations/special_room_location.dart | 3 +- .../entities/locations/store_location.dart | 4 +-- .../entities/locations/unknown_location.dart | 4 +-- .../entities/locations/vending_machine.dart | 4 +-- .../model/entities/locations/wc_location.dart | 4 +-- .../uni_app/lib/view/map/widgets/icons.dart | 30 ------------------ .../uni_app/lib/view/map/widgets/marker.dart | 5 +-- packages/uni_app/pubspec.yaml | 3 -- packages/uni_ui/lib/icons.dart | 12 +++++++ 16 files changed, 37 insertions(+), 56 deletions(-) delete mode 100644 packages/uni_app/assets/fonts/LocationIcons.ttf delete mode 100644 packages/uni_app/lib/view/map/widgets/icons.dart diff --git a/packages/uni_app/assets/fonts/LocationIcons.ttf b/packages/uni_app/assets/fonts/LocationIcons.ttf deleted file mode 100644 index 9c1849fb3657caca7020e1e924e6e9763ba01353..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2472 zcmd^B-)|IE6h3!myM_K(*s?#`5^#36F0i!Q-Pv|2SW5X-(xjl-T^l4O%XB;2U6`F& zW~WHesxcB1X^c;3Vq#)qs85;@6OBP1#pr`*!kYnNOjuvgM0{yz*YD0w5EAeoaHjX( z?|kRG_nvd^%xsAfQ9Hd(A|;MwpFbd7eEK{Q8v&bpHajSfNs}+bUV=Scs3_IncioS{ zzK{0FoVHN>?B=y;BJpz~VYH+wvmyHND`;<{rAr`MN5vO_ucGypD%QDR6^Phx=(;td zpa|R9Eara<+fz}_Rhf_W!QTyA(v^xDUHR)RqLx3Q8Lb*MYbE{mA4Kc>;XhA^UCcC2 zp|8W{J##O3u!Ck+R*XT(0C?& zAr`xk&WsCh_&pm#p^YB&s~@iG6nwovoBR3&%skn>dduI}%Wn1d`T5g8jjMu*w`mIu z5Ay|rexG|M3rF{|F*X$4Hk{rS8BX~lfuL~4=lA;(kz6Ff!9H*Fa@4zz`MMJ6MAu{_ znT$-@@LK*%E_WtxKcS7Uk@s=bY9mR=o;HQNAtrBSJ`W4W+0Yo<=5t5CnI*3 zTRWNK2Z_^Z+z~GcH-K7cGf^-!9Ae03IOO$hWzR5qjO|6;(|eI|tA2${)gZ zLvbeDMY#83-kzRtclSGMSJ<(|)i06XGP}1N-Ywi%zRn%tp0!_oS^>SXyvzrlMHk(4 z-g2S|*f=q}?}XLF0a63hXk%LBc!7R^} zENT0}9g>`oWoc$XGC-AeMU#|yt7MopXI{s;OA&EDxb}c22geqEs}EiZtQuB~?x1 zlv=RjCCjSr8612#20BOvRcV1tDx>Bl)LNqLV0U1Y@w+4AmS_eF3CgCbOgbr~fl+84 zGfK9n274T>2oGm1Qf++!UY+8^#|pmmT)`Rc*KivTd{e*$tZ5+@u9&pv=v1Q&v~yTh zgV&@d>z?eHwxg*zD@&Bbu8IFx=?L_?J%`7m(k$X`R-r~`5sM7gLMA*L6VX Date: Fri, 17 Jan 2025 12:39:18 +0000 Subject: [PATCH 6/7] map search field shadow and padding --- packages/uni_app/lib/view/map/map.dart | 75 ++++++++++++++------------ 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/packages/uni_app/lib/view/map/map.dart b/packages/uni_app/lib/view/map/map.dart index 9a6ab35d8..ce64d9d80 100644 --- a/packages/uni_app/lib/view/map/map.dart +++ b/packages/uni_app/lib/view/map/map.dart @@ -48,11 +48,7 @@ class MapPageState extends GeneralPageViewState { S.of(context).nav_title(NavigationItem.navLocations.route); @override - AppTopNavbar? getTopNavbar(BuildContext context) { - return const AppTopNavbar( - heightSize: Size.fromHeight(0), - ); - } + AppTopNavbar? getTopNavbar(BuildContext context) => null; } class MapPageView extends StatefulWidget { @@ -134,7 +130,7 @@ class MapPageViewState extends State { ], children: [ TileLayer( - // TODO(thePeras): Ideia - change layer in dark theme + // TODO(thePeras): Idea: change layer in dark theme urlTemplate: 'https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png', subdomains: const ['a', 'b', 'c'], @@ -161,39 +157,48 @@ class MapPageViewState extends State { ), ), ), - Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: TextFormField( - key: searchFormKey, - onChanged: (text) { - setState(() { - searchTerms = removeDiacritics(text.trim().toLowerCase()); - }); - }, - decoration: InputDecoration( - filled: true, - fillColor: Theme.of(context).colorScheme.secondary, - prefixIcon: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: SvgPicture.asset( - 'assets/images/logo_dark.svg', - semanticsLabel: 'search', - width: 10, + SafeArea( + child: Column( + children: [ + Padding( + padding: + const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: PhysicalModel( + borderRadius: BorderRadius.circular(10), + color: Colors.white, + elevation: 3, + child: TextFormField( + key: searchFormKey, + onChanged: (text) { + setState(() { + searchTerms = + removeDiacritics(text.trim().toLowerCase()); + }); + }, + decoration: InputDecoration( + filled: true, + fillColor: Theme.of(context).colorScheme.secondary, + prefixIcon: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: SvgPicture.asset( + 'assets/images/logo_dark.svg', + semanticsLabel: 'search', + width: 10, + ), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(10), + borderSide: BorderSide.none, + ), + contentPadding: const EdgeInsets.all(10), + hintText: '${S.of(context).search}...', ), ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(10), - borderSide: BorderSide.none, - ), - contentPadding: const EdgeInsets.all(10), - hintText: '${S.of(context).search}...', ), ), - ), - ], - ) + ], + ), + ), ], ); } From 6bb02d37347cac48cdf8b5554cbbec844002241c Mon Sep 17 00:00:00 2001 From: thePeras Date: Fri, 17 Jan 2025 12:46:52 +0000 Subject: [PATCH 7/7] fix screen resize when keyboard is opened --- .../lib/view/common_widgets/pages_layouts/general/general.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/uni_app/lib/view/common_widgets/pages_layouts/general/general.dart b/packages/uni_app/lib/view/common_widgets/pages_layouts/general/general.dart index 72b22e451..6d9027a4e 100644 --- a/packages/uni_app/lib/view/common_widgets/pages_layouts/general/general.dart +++ b/packages/uni_app/lib/view/common_widgets/pages_layouts/general/general.dart @@ -102,12 +102,14 @@ abstract class GeneralPageViewState extends State { context, _loading ? const Center(child: CircularProgressIndicator()) + // TODO: Add 100 of padding bottom : getBody(context), ); } Widget getScaffold(BuildContext context, Widget body) { return Scaffold( + resizeToAvoidBottomInset: false, backgroundColor: Theme.of(context).colorScheme.surface, appBar: getTopNavbar(context), extendBody: true,