diff --git a/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart b/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart index 6ad860b629..31dff6608a 100644 --- a/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart +++ b/mobile-v3/lib/src/app/dashboard/widgets/analytics_card.dart @@ -95,7 +95,7 @@ class AnalyticsCard extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(measurement.siteDetails!.locationName ?? "", + Text(measurement.siteDetails!.name ?? "", style: TextStyle( fontSize: 28, fontWeight: FontWeight.w700, diff --git a/mobile-v3/lib/src/app/dashboard/widgets/analytics_forecast_widget.dart b/mobile-v3/lib/src/app/dashboard/widgets/analytics_forecast_widget.dart index d36eea1c07..0ff51b567f 100644 --- a/mobile-v3/lib/src/app/dashboard/widgets/analytics_forecast_widget.dart +++ b/mobile-v3/lib/src/app/dashboard/widgets/analytics_forecast_widget.dart @@ -27,16 +27,14 @@ class _AnalyticsForecastWidgetState extends State { double _getResponsiveHeight(BuildContext context) { final screenHeight = MediaQuery.of(context).size.height; - // Calculate height based on screen size, with minimum and maximum bounds - final height = screenHeight * 0.1; // 10% of screen height - return height.clamp(60.0, 100.0); // Min 60, max 100 + final height = screenHeight * 0.1; + return height.clamp(60.0, 100.0); } double _getResponsiveIconSize(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; - // Calculate icon size based on screen width - final iconSize = screenWidth * 0.04; // 4% of screen width - return iconSize.clamp(20.0, 30.0); // Min 20, max 30 + final iconSize = screenWidth * 0.04; + return iconSize.clamp(20.0, 30.0); } double _getResponsiveMargin(BuildContext context) { diff --git a/mobile-v3/lib/src/app/learn/pages/lesson_finished.dart b/mobile-v3/lib/src/app/learn/pages/lesson_finished.dart index e7098c6bfc..8ef0b579c4 100644 --- a/mobile-v3/lib/src/app/learn/pages/lesson_finished.dart +++ b/mobile-v3/lib/src/app/learn/pages/lesson_finished.dart @@ -11,7 +11,7 @@ class LessonFinishedWidget extends StatelessWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Text("👋🏼 Great Job Jordan!", + Text("👋🏼 Great Job !", style: TextStyle(fontSize: 20, fontWeight: FontWeight.w700)), Text( "You can invite your friends to learn a thing about Air Pollution", diff --git a/mobile-v3/lib/src/app/map/pages/map_page.dart b/mobile-v3/lib/src/app/map/pages/map_page.dart index 1e3175c95b..7809caaf72 100644 --- a/mobile-v3/lib/src/app/map/pages/map_page.dart +++ b/mobile-v3/lib/src/app/map/pages/map_page.dart @@ -40,12 +40,43 @@ class _MapScreenState extends State String currentFilter = "All"; List allMeasurements = []; + List localSearchResults = []; + + List searchAirQualityLocations( + String query, List measurements) { + query = query.toLowerCase(); + return measurements.where((measurement) { + if (measurement.siteDetails != null) { + // Search through multiple location fields + return (measurement.siteDetails!.city?.toLowerCase().contains(query) ?? + false) || + (measurement.siteDetails!.locationName + ?.toLowerCase() + .contains(query) ?? + false) || + (measurement.siteDetails!.name?.toLowerCase().contains(query) ?? + false) || + (measurement.siteDetails!.searchName + ?.toLowerCase() + .contains(query) ?? + false) || + (measurement.siteDetails!.formattedName + ?.toLowerCase() + .contains(query) ?? + false) || + (measurement.siteDetails!.town?.toLowerCase().contains(query) ?? + false) || + (measurement.siteDetails!.district?.toLowerCase().contains(query) ?? + false); + } + return false; + }).toList(); + } void filterByCountry(String country, List measurements) { setState(() { filteredMeasurements = measurements.where((measurement) { if (measurement.siteDetails != null) { - print(measurement.siteDetails!.country); return measurement.siteDetails!.country == country; } return false; @@ -426,53 +457,7 @@ class _MapScreenState extends State color: AppColors .boldHeadlineColor)), Row(children: [ - // Container( - // decoration: BoxDecoration( - // color: AppColors - // .highlightColor, - // borderRadius: - // BorderRadius - // .circular( - // 100)), - // height: 40, - // width: 52, - // child: Center( - // child: Padding( - // padding: - // const EdgeInsets - // .only( - // left: 8.0), - // child: Icon( - // size: 20, - // Icons - // .arrow_back_ios, - // color: AppColors - // .boldHeadlineColor, - // ), - // ), - // ), - // ), SizedBox(width: 8), - // Container( - // decoration: BoxDecoration( - // color: AppColors - // .highlightColor, - // borderRadius: - // BorderRadius - // .circular( - // 100)), - // height: 40, - // width: 52, - // child: Center( - // child: Icon( - // Icons - // .arrow_forward_ios, - // size: 20, - // color: AppColors - // .boldHeadlineColor, - // ), - // ), - // ) ]) ], ), @@ -781,12 +766,23 @@ class _MapScreenState extends State controller: searchController, onChanged: (value) { print(value); - if (value == "") { + if (value.isEmpty) { googlePlacesBloc! .add(ResetGooglePlaces()); + setState(() { + // Reset local search results + localSearchResults = []; + }); + } else { + googlePlacesBloc! + .add(SearchPlace(value)); + setState(() { + localSearchResults = + searchAirQualityLocations( + value, + allMeasurements); + }); } - googlePlacesBloc! - .add(SearchPlace(value)); }, style: TextStyle(fontSize: 14), onTap: () => toggleModal(true), @@ -820,7 +816,11 @@ class _MapScreenState extends State clearGooglePlaces(), child: Icon( Icons.close, - color: Theme.of(context).textTheme.headlineLarge!.color, + color: Theme.of( + context) + .textTheme + .headlineLarge! + .color, )); } return SizedBox(); @@ -860,53 +860,138 @@ class _MapScreenState extends State ); } else if (placesState is SearchLoaded) { - if (placesState.response - .predictions.isEmpty) { - return Center( - child: Text( - "No results found", - style: TextStyle( - fontSize: 18, - fontWeight: - FontWeight.w500, - color: AppColors - .boldHeadlineColor), - ), - ); - } - return ListView.separated( - separatorBuilder: - (context, index) { - return Divider( - indent: 50, - ); - }, - padding: - const EdgeInsets.only(), - shrinkWrap: true, - itemCount: placesState - .response - .predictions - .length, - itemBuilder: - (context, index) { - Prediction prediction = - placesState.response - .predictions[index]; + return Column( + children: [ + // Show local AirQuality matches first + if (localSearchResults + .isNotEmpty) ...[ + Text( + "Air Quality Monitoring Locations", + style: TextStyle( + fontWeight: + FontWeight + .bold)), + ListView.separated( + shrinkWrap: true, + itemCount: + localSearchResults + .length, + separatorBuilder: + (context, index) => + Divider( + indent: 50), + itemBuilder: + (context, index) { + Measurement + measurement = + localSearchResults[ + index]; + return GestureDetector( + onTap: () => + viewDetails( + measurement: + measurement), + child: + LocationDisplayWidget( + title: measurement + .siteDetails! + .locationName ?? + "", + subTitle: measurement + .siteDetails! + .name ?? + "", + ), + ); + }), + Divider(), + ], - return GestureDetector( - onTap: () => viewDetails( - placeName: prediction - .description), - child: LocationDisplayWidget( - title: prediction - .description, - subTitle: prediction - .structuredFormatting - .mainText), - ); - }); + // Then show Google Places results + Text("Other Locations", + style: TextStyle( + fontWeight: + FontWeight.bold)), + ListView.separated( + shrinkWrap: true, + itemCount: placesState + .response + .predictions + .length, + separatorBuilder: + (context, index) => + Divider( + indent: 50), + itemBuilder: + (context, index) { + Prediction prediction = + placesState.response + .predictions[ + index]; + return GestureDetector( + onTap: () => viewDetails( + placeName: prediction + .description), + child: LocationDisplayWidget( + title: prediction + .description, + subTitle: prediction + .structuredFormatting + .mainText), + ); + }), + ], + ); } + // } else if (placesState + // is SearchLoaded) { + // if (placesState.response + // .predictions.isEmpty) { + // return Center( + // child: Text( + // "No results found", + // style: TextStyle( + // fontSize: 18, + // fontWeight: + // FontWeight.w500, + // color: AppColors + // .boldHeadlineColor), + // ), + // ); + // } + // return ListView.separated( + // separatorBuilder: + // (context, index) { + // return Divider( + // indent: 50, + // ); + // }, + // padding: + // const EdgeInsets.only(), + // shrinkWrap: true, + // itemCount: placesState + // .response + // .predictions + // .length, + // itemBuilder: + // (context, index) { + // Prediction prediction = + // placesState.response + // .predictions[index]; + + // return GestureDetector( + // onTap: () => viewDetails( + // placeName: prediction + // .description), + // child: LocationDisplayWidget( + // title: prediction + // .description, + // subTitle: prediction + // .structuredFormatting + // .mainText), + // ); + // }); + // } return Expanded( child: Column( crossAxisAlignment: @@ -1012,90 +1097,64 @@ class _MapScreenState extends State Expanded( child: Builder( - builder: (context) { - if (currentFilter == - "All") { - return ListView - .separated( - separatorBuilder: - (context, index) { - return Divider( - indent: 50, - ); - }, - padding: - const EdgeInsets - .only(), - shrinkWrap: true, - itemCount: 15, - itemBuilder: - (context, index) { - Measurement - measurement = - allMeasurements[ - index]; + builder: (context) { + List + measurements = + currentFilter == + "All" + ? allMeasurements + : filteredMeasurements; + + // If the list is empty, show a message instead of throwing an error + if (measurements + .isEmpty) { + return Center( + child: Text( + "No measurements available"), + ); + } - return GestureDetector( - onTap: () => - viewDetails( - measurement: - measurement), - child: - LocationDisplayWidget( - title: measurement - .siteDetails! - .city ?? - "", - subTitle: measurement - .siteDetails! - .locationName ?? - "", - )); - }, - ); - } else { return ListView .separated( separatorBuilder: - (context, index) { - return Divider( - indent: 50, - ); - }, + (context, + index) => + const Divider( + indent: + 50), padding: - const EdgeInsets - .only(), + EdgeInsets.zero, shrinkWrap: true, itemCount: - filteredMeasurements + measurements .length, itemBuilder: (context, index) { Measurement measurement = - filteredMeasurements[ + measurements[ index]; - return GestureDetector( - onTap: () => - viewDetails( - measurement: - measurement), - child: - LocationDisplayWidget( - title: measurement - .siteDetails! - .city ?? - "", - subTitle: measurement - .siteDetails! - .locationName ?? - "", - )); + onTap: () => + viewDetails( + measurement: + measurement), + child: + LocationDisplayWidget( + title: measurement + .siteDetails + ?.city ?? + "Unknown City", + subTitle: measurement + .siteDetails + ?.name ?? + "Unknown Location", + ), + ); }, ); - } - }), + }, + ), ) ], ),