Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 63 additions & 56 deletions src/mobile/lib/src/app/dashboard/pages/dashboard_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,7 @@ class _DashboardPageState extends State<DashboardPage> with UiLoggy {
},
),
),
SliverToBoxAdapter(
child: _buildContentForCurrentView(isGuest: isGuest),
),
_buildContentForCurrentView(isGuest: isGuest),
],
),
),
Expand Down Expand Up @@ -180,56 +178,58 @@ class _DashboardPageState extends State<DashboardPage> with UiLoggy {
return BlocBuilder<DashboardBloc, DashboardState>(
builder: (context, state) {
if (state is DashboardLoading && state.previousState == null) {
return DashboardLoadingPage();
return const SliverToBoxAdapter(child: DashboardLoadingPage());
}

if (state is DashboardLoadingError && !state.hasCache) {
final isOffline = state.isOffline;
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
isOffline ? Icons.cloud_off : Icons.error_outline,
size: 64,
color: Colors.grey,
),
SizedBox(height: 16),
TranslatedText(
isOffline
? "Couldn't connect to the internet"
: "Couldn't load air quality data",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Theme.of(context).textTheme.headlineMedium?.color,
return SliverToBoxAdapter(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
isOffline ? Icons.cloud_off : Icons.error_outline,
size: 64,
color: Colors.grey,
),
),
SizedBox(height: 8),
TranslatedText(
isOffline
? "Please check your connection and try again"
: "Something went wrong. Please try again later",
style: TextStyle(
fontSize: 16,
color: Theme.of(context).textTheme.bodyMedium?.color,
SizedBox(height: 16),
TranslatedText(
isOffline
? "Couldn't connect to the internet"
: "Couldn't load air quality data",
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Theme.of(context).textTheme.headlineMedium?.color,
),
),
),
SizedBox(height: 24),
ElevatedButton.icon(
onPressed: () {
context
.read<DashboardBloc>()
.add(LoadDashboard(forceRefresh: true));
},
icon: Icon(Icons.refresh),
label: TranslatedText('Try Again'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryColor,
foregroundColor: Colors.white,
SizedBox(height: 8),
TranslatedText(
isOffline
? "Please check your connection and try again"
: "Something went wrong. Please try again later",
style: TextStyle(
fontSize: 16,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
),
],
SizedBox(height: 24),
ElevatedButton.icon(
onPressed: () {
context
.read<DashboardBloc>()
.add(LoadDashboard(forceRefresh: true));
},
icon: Icon(Icons.refresh),
label: TranslatedText('Try Again'),
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryColor,
foregroundColor: Colors.white,
),
),
],
),
),
);
}
Expand All @@ -244,16 +244,21 @@ class _DashboardPageState extends State<DashboardPage> with UiLoggy {
'User has ${state.selectedLocationIds.length} selected locations');
}

return MyPlacesView(
userPreferences: state.userPreferences,
return SliverToBoxAdapter(
child: MyPlacesView(
userPreferences: state.userPreferences,
),
);

case DashboardView.nearYou:
return NearbyView(
onNavigateToFavorites: () => setView(DashboardView.favorites),
onExploreCities: isGuest
? () => setView(DashboardView.explore)
: null,
return SliverToBoxAdapter(
child: NearbyView(
onNavigateToFavorites: () =>
setView(DashboardView.favorites),
onExploreCities: isGuest
? () => setView(DashboardView.explore)
: null,
),
);

case DashboardView.country:
Expand All @@ -265,8 +270,10 @@ class _DashboardPageState extends State<DashboardPage> with UiLoggy {
return MeasurementsList(measurements: countryMeasurements);

case DashboardView.explore:
return ExploreCountriesView(
measurements: state.response.measurements ?? [],
return SliverToBoxAdapter(
child: ExploreCountriesView(
measurements: state.response.measurements ?? [],
),
);

default:
Expand All @@ -277,7 +284,7 @@ class _DashboardPageState extends State<DashboardPage> with UiLoggy {
}
}

return DashboardLoadingPage();
return const SliverToBoxAdapter(child: DashboardLoadingPage());
},
);
}
Expand Down
13 changes: 5 additions & 8 deletions src/mobile/lib/src/app/dashboard/widgets/measurements_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@ class MeasurementsList extends StatelessWidget {

@override
Widget build(BuildContext context) {
return ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: measurements.length,
shrinkWrap: true,
padding: EdgeInsets.zero, // Remove any default padding
itemBuilder: (context, index) {
return AnalyticsCard(measurements[index]);
}
return SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => AnalyticsCard(measurements[index]),
childCount: measurements.length,
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,70 @@ import 'package:flutter_svg/flutter_svg.dart';
import 'package:loggy/loggy.dart';
import 'package:airqo/src/app/dashboard/models/airquality_response.dart';
import 'package:airqo/src/app/dashboard/pages/forecast_overview_page.dart';
import 'package:airqo/src/app/shared/widgets/loading_widget.dart';
import 'package:airqo/src/meta/utils/colors.dart';
import 'package:airqo/src/meta/utils/forecast_utils.dart';
import 'package:airqo/src/meta/utils/utils.dart';

/// Shimmer skeleton matching [NearbyMeasurementCard]'s layout, shown per-slot
/// while nearby measurements are still being resolved (location + dashboard
/// fetch), so the "Near You" tab doesn't show a single blocking spinner.
class NearbyMeasurementCardLoader extends StatelessWidget {
const NearbyMeasurementCardLoader({super.key});

@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: AppSurfaceColors.elevatedCardDecoration(context),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding:
const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ShimmerText(width: 140, height: 20),
SizedBox(height: 8),
ShimmerText(width: 100, height: 12),
],
),
),
],
),
),
Divider(thickness: .5, color: Theme.of(context).dividerColor),
Padding(
padding:
const EdgeInsets.only(left: 16, right: 16, bottom: 16, top: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ShimmerText(width: 70, height: 14),
SizedBox(height: 8),
ShimmerText(width: 90, height: 30),
],
),
ShimmerContainer(height: 86, width: 86, borderRadius: 100),
],
),
),
],
),
);
}
}

class NearbyMeasurementCard extends StatelessWidget with UiLoggy {
final Measurement measurement;
final String? fallbackLocationName;
Expand Down
22 changes: 6 additions & 16 deletions src/mobile/lib/src/app/dashboard/widgets/nearby_view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -455,22 +455,12 @@ class _NearbyViewState extends State<NearbyView> with UiLoggy {
}

if (isLoading && _nearbyMeasurementsWithDistance.isEmpty) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 120),
CircularProgressIndicator(color: AppColors.primaryColor),
const SizedBox(height: 16),
TranslatedText(
"Getting your location...",
style: TextStyle(
fontSize: 16,
color: Theme.of(context).textTheme.bodyMedium?.color,
),
),
],
),
return ListView.builder(
itemCount: _maxNearbyLocations,
padding: const EdgeInsets.only(top: 8, bottom: 16),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => const NearbyMeasurementCardLoader(),
);
}

Expand Down
10 changes: 9 additions & 1 deletion src/mobile/lib/src/app/map/utils/map_marker_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import 'package:flutter/painting.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

class MapMarkerBuilder {
/// Rasterized marker icon size, in logical pixels. The air quality badge
/// SVGs are 96x96 circular icons — 10x10 (the previous value) rendered
/// as little more than a dot on the map.
static const Size _markerIconSize = Size(44, 44);

Future<List<Marker>> buildMarkers({
required List<Measurement> measurements,
required ValueChanged<Measurement> onMeasurementTap,
Expand Down Expand Up @@ -42,8 +47,11 @@ class MapMarkerBuilder {
onTap: () => onTap(measurement),
icon: await bitmapDescriptorFromSvgAsset(
resolvedPath,
const Size(10, 10),
_markerIconSize,
),
// These icons are circular badges, not teardrop pins, so center the
// icon on the coordinate instead of the default bottom-anchor.
anchor: const Offset(0.5, 0.5),
position: LatLng(
measurement.siteDetails!.approximateLatitude!,
measurement.siteDetails!.approximateLongitude!,
Expand Down
Loading
Loading