import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map_cancellable_tile_provider/flutter_map_cancellable_tile_provider.dart'; import 'package:flutter_map_marker_cluster/flutter_map_marker_cluster.dart'; import 'package:latlong2/latlong.dart'; import 'package:provider/provider.dart'; import '../model/flood_station.dart'; import '../services/flood_station_provider.dart'; import '../widgets/custom_marker.dart'; import '../widgets/map_popup.dart'; import 'flood_station_page.dart'; class MapPage extends StatefulWidget { const MapPage({super.key}); static const routeName = '/map'; @override State createState() => _MapPageState(); } class _MapPageState extends State { final _mapController = MapController(); late FloodStationProvider _floodStationProvider; bool _loading = false; @override Widget build(BuildContext context) { _floodStationProvider = context.watch(); if (_loading == true) { return Center( child: CircularProgressIndicator(), ); } else if (_floodStationProvider.allStations.isEmpty) { return Center( child: ElevatedButton( onPressed: () { setState(() { _loading = true; }); _floodStationProvider .loadAllStations() .whenComplete(() => setState(() { _loading = false; })); }, child: Text('Load Map'), ), ); } return FlutterMap( mapController: _mapController, options: MapOptions( cameraConstraint: CameraConstraint.containCenter( bounds: LatLngBounds.fromPoints(_floodStationProvider.allStations .map( (e) => LatLng(e.lat, e.long), ) .toList())), initialCenter: LatLng(54.81, -4.42), initialZoom: 6, ), children: [ _openStreetMapTileLayer, MarkerClusterLayerWidget( options: MarkerClusterLayerOptions( maxClusterRadius: 45, size: Size(30, 30), alignment: Alignment.center, padding: EdgeInsets.all(50), maxZoom: 15, markers: _stationsAsMarkers(_floodStationProvider.allStations), builder: _clusterMarkerBuilder), ) ], ); } // builds the clustered marker Widget _clusterMarkerBuilder(BuildContext context, List markers) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), color: Theme.of(context).colorScheme.tertiary, ), child: Center( child: Text( markers.length.toString(), style: TextStyle(color: Theme.of(context).colorScheme.onTertiary), ), ), ); } // gets a list of markers from the list of all stations List _stationsAsMarkers(List stations) { return stations .map( (station) => Marker( alignment: Alignment.center, point: LatLng(station.lat, station.long), child: CustomMarker( onTap: () => _markerTapped(station), ), ), ) .toList(); } void _markerTapped(FloodStation station) { showDialog( context: context, builder: (context) => MapPopup( station: station, onShowTapped: () { _floodStationProvider.selectedStation = station; Navigator.of(context).pushNamed(FloodStationPage.routeName); }), ); } } TileLayer get _openStreetMapTileLayer => TileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', userAgentPackageName: 'dev.fleaflet.flutter_map.example', tileProvider: CancellableNetworkTileProvider(), );