670 lines
27 KiB
Dart
670 lines
27 KiB
Dart
// widgets/vigna_map_widget.dart
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_map/flutter_map.dart';
|
|
import 'package:get/get.dart';
|
|
import 'package:get/get_core/src/get_main.dart';
|
|
import 'package:latlong2/latlong.dart';
|
|
import 'package:u_vineyard/entities/coordinates.dart';
|
|
import 'package:u_vineyard/entities/vineyard.dart';
|
|
import 'package:u_vineyard/template/src/providers/auth.provider.dart';
|
|
import 'package:u_vineyard/template/src/widgets/card.widget.dart';
|
|
|
|
// Definizione del tipo di callback per i tap sulla mappa
|
|
typedef MapTapCallback = void Function(TapPosition tapPosition, LatLng latLng);
|
|
|
|
class MainMapWidget extends StatefulWidget {
|
|
const MainMapWidget({super.key});
|
|
|
|
@override
|
|
State<MainMapWidget> createState() => _MainMapWidgetState();
|
|
}
|
|
|
|
class _MainMapWidgetState extends State<MainMapWidget> {
|
|
final MapController _mapController = MapController();
|
|
final Map<String, String> _mapUrlTemplates = {
|
|
'Standard': 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
'Terreno (OpenTopoMap)': 'https://opentopomap.org/{z}/{x}/{y}.png',
|
|
'Satellite (ESRI)': 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
|
};
|
|
|
|
final Map<String, Widget> _mapAttributions = {
|
|
'Standard': const Text('© OpenStreetMap contributors'),
|
|
'Terreno (OpenTopoMap)': const Text('Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © OpenTopoMap (CC-BY-SA)'),
|
|
'Satellite (ESRI)': const Text('Tiles © Esri — Source: Esri, i-cubed, USDA FSA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'),
|
|
};
|
|
|
|
String? _selectedMapType = 'Standard';
|
|
double _currentZoom = 13.0; // Questo zoom verrà sovrascritto se ci sono aree
|
|
|
|
// Variabile per tenere traccia se la mappa è già stata centrata una volta
|
|
bool _mapCenteredInitially = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
// Non inizializzare qui _vineyardList o _areas
|
|
}
|
|
|
|
void _zoomIn() {
|
|
setState(() {
|
|
_currentZoom += 1;
|
|
_mapController.move(_mapController.camera.center, _currentZoom);
|
|
});
|
|
}
|
|
|
|
void _zoomOut() {
|
|
setState(() {
|
|
_currentZoom -= 1;
|
|
_mapController.move(_mapController.camera.center, _currentZoom);
|
|
});
|
|
}
|
|
|
|
// Funzione per calcolare i limiti e centrare la mappa
|
|
void _fitMapToPolygons(List<Map<int, Coordinates>> areas) {
|
|
if (areas.isEmpty) {
|
|
// Se non ci sono aree, centra sulla posizione predefinita
|
|
_mapController.move(const LatLng(45.4642, 9.1900), 10.0);
|
|
return;
|
|
}
|
|
|
|
// Raccogli tutti i punti da tutte le aree
|
|
List<LatLng> allPoints = [];
|
|
for (var areaMap in areas) {
|
|
for (var coord in areaMap.values) {
|
|
allPoints.add(LatLng(coord.latitude, coord.longitude));
|
|
}
|
|
}
|
|
|
|
if (allPoints.isEmpty) {
|
|
// Se per qualche motivo non ci sono punti validi, torna al default
|
|
_mapController.move(const LatLng(45.4642, 9.1900), 10.0);
|
|
return;
|
|
}
|
|
|
|
// Calcola i limiti (min/max latitudine e longitudine)
|
|
double? minLat, maxLat, minLon, maxLon;
|
|
|
|
for (var point in allPoints) {
|
|
if (minLat == null || point.latitude < minLat) minLat = point.latitude;
|
|
if (maxLat == null || point.latitude > maxLat) maxLat = point.latitude;
|
|
if (minLon == null || point.longitude < minLon) minLon = point.longitude;
|
|
if (maxLon == null || point.longitude > maxLon) maxLon = point.longitude;
|
|
}
|
|
|
|
if (minLat != null && maxLat != null && minLon != null && maxLon != null) {
|
|
// Crea un LatLngBounds
|
|
final LatLngBounds bounds = LatLngBounds(
|
|
LatLng(minLat, minLon),
|
|
LatLng(maxLat, maxLon),
|
|
);
|
|
|
|
// Adatta la camera per mostrare tutti i punti
|
|
_mapController.fitCamera(
|
|
CameraFit.bounds(
|
|
bounds: bounds,
|
|
padding: const EdgeInsets.all(50.0), // Aggiungi un po' di padding
|
|
),
|
|
);
|
|
// Aggiorna lo zoom corrente dopo il fit
|
|
_currentZoom = _mapController.camera.zoom;
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final AuthProvider authProvider = Get.find<AuthProvider>();
|
|
|
|
return CardWidget(
|
|
height: 700,
|
|
child: Obx(() {
|
|
final List<Vineyard> vineyardList = authProvider.profile?.owners?.expand((owner) => owner.vineyards ?? <Vineyard>[]).cast<Vineyard>().toList() ?? [];
|
|
final List<Map<int, Coordinates>> areas = vineyardList
|
|
.map((vineyard) => vineyard.area)
|
|
.whereType<Map<int, Coordinates>>()
|
|
.toList();
|
|
|
|
// Centra la mappa solo una volta o quando le aree cambiano significativamente
|
|
// Per semplicità, la centreremo solo la prima volta che le aree vengono caricate
|
|
// o se il flag _mapCenteredInitially è falso e ci sono aree.
|
|
if (areas.isNotEmpty && !_mapCenteredInitially) {
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
_fitMapToPolygons(areas);
|
|
_mapCenteredInitially = true; // Imposta il flag a true dopo il primo centraggio
|
|
});
|
|
} else if (areas.isEmpty && _mapCenteredInitially) {
|
|
// Se le aree diventano vuote dopo essere state caricate, resetta il flag
|
|
_mapCenteredInitially = false;
|
|
}
|
|
|
|
|
|
// Logica per initialCenter della mappa (usata solo alla prima costruzione)
|
|
// Dopo il primo render, _mapController.move gestirà il centro e lo zoom
|
|
LatLng initialMapCenter;
|
|
if (vineyardList.isNotEmpty && vineyardList.first.coordinates != null) {
|
|
initialMapCenter = LatLng(vineyardList.first.coordinates!.latitude, vineyardList.first.coordinates!.longitude);
|
|
} else {
|
|
initialMapCenter = const LatLng(45.4642, 9.1900); // Centro di default (Milano)
|
|
}
|
|
|
|
return Column(
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
'Vigne',
|
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.green[800]),
|
|
),
|
|
DropdownButton<String>(
|
|
value: _selectedMapType,
|
|
items: const [
|
|
DropdownMenuItem(value: 'Standard', child: Text('Standard')),
|
|
DropdownMenuItem(value: 'Terreno (OpenTopoMap)', child: Text('Terreno')),
|
|
DropdownMenuItem(value: 'Satellite (ESRI)', child: Text('Satellite')),
|
|
],
|
|
onChanged: (String? newValue) {
|
|
setState(() {
|
|
_selectedMapType = newValue;
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 16),
|
|
Expanded(
|
|
child: Container(
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.grey),
|
|
borderRadius: BorderRadius.circular(8.0),
|
|
),
|
|
child: FlutterMap(
|
|
mapController: _mapController,
|
|
options: MapOptions(
|
|
initialCenter: initialMapCenter,
|
|
initialZoom: _currentZoom, // Usa _currentZoom che può essere aggiornato da _fitMapToPolygons
|
|
),
|
|
children: [
|
|
TileLayer(
|
|
urlTemplate: _mapUrlTemplates[_selectedMapType]!,
|
|
userAgentPackageName: 'com.example.vigna_app',
|
|
),
|
|
RichAttributionWidget(
|
|
attributions: [
|
|
TextSourceAttribution(
|
|
(_mapAttributions[_selectedMapType]! as Text).data!,
|
|
onTap: () => print('Attribution tapped: ${(_mapAttributions[_selectedMapType]! as Text).data}'),
|
|
),
|
|
],
|
|
),
|
|
MarkerLayer(
|
|
markers: vineyardList.map((vineyard) {
|
|
return Marker(
|
|
width: 40.0,
|
|
height: 40.0,
|
|
point: vineyard.coordinates != null
|
|
? LatLng(vineyard.coordinates!.latitude, vineyard.coordinates!.longitude)
|
|
: const LatLng(0, 0),
|
|
child: Tooltip(
|
|
message: vineyard.name,
|
|
child: const Icon(Icons.grass, color: Colors.green, size: 30.0),
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
Positioned(
|
|
top: 40,
|
|
right: 10,
|
|
child: Column(
|
|
children: [
|
|
FloatingActionButton(
|
|
heroTag: 'zoomIn',
|
|
mini: true,
|
|
onPressed: _zoomIn,
|
|
child: const Icon(Icons.add),
|
|
),
|
|
const SizedBox(height: 8),
|
|
FloatingActionButton(
|
|
heroTag: 'zoomOut',
|
|
mini: true,
|
|
onPressed: _zoomOut,
|
|
child: const Icon(Icons.remove),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
if (areas.isNotEmpty)
|
|
PolygonLayer(
|
|
polygons: areas.expand((areaMap) {
|
|
final List<LatLng> polygonPoints = areaMap.values
|
|
.map((coord) => LatLng(coord.latitude, coord.longitude))
|
|
.toList();
|
|
|
|
if (polygonPoints.isNotEmpty && polygonPoints.first != polygonPoints.last) {
|
|
polygonPoints.add(polygonPoints.first);
|
|
}
|
|
|
|
return [
|
|
if (polygonPoints.length >= 3)
|
|
Polygon(
|
|
points: polygonPoints,
|
|
color: Colors.blue.withOpacity(0.3),
|
|
borderStrokeWidth: 2,
|
|
borderColor: Colors.blue[900]!,
|
|
),
|
|
];
|
|
}).toList(),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
// class MainMapWidget extends StatefulWidget {
|
|
// const MainMapWidget({super.key});
|
|
|
|
// @override
|
|
// State<MainMapWidget> createState() => _MainMapWidgetState();
|
|
// }
|
|
|
|
// class _MainMapWidgetState extends State<MainMapWidget> {
|
|
// // Le variabili di stato devono essere qui
|
|
// final MapController _mapController = MapController();
|
|
// final Map<String, String> _mapUrlTemplates = {
|
|
// 'Standard': 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
// 'Terreno (OpenTopoMap)': 'https://opentopomap.org/{z}/{x}/{y}.png',
|
|
// 'Satellite (ESRI)': 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
|
// };
|
|
|
|
// final Map<String, Widget> _mapAttributions = {
|
|
// 'Standard': const Text('© OpenStreetMap contributors'),
|
|
// 'Terreno (OpenTopoMap)': const Text('Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © OpenTopoMap (CC-BY-SA)'),
|
|
// 'Satellite (ESRI)': const Text('Tiles © Esri — Source: Esri, i-cubed, USDA FSA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'),
|
|
// };
|
|
|
|
// String? _selectedMapType = 'Standard'; // Tipo di mappa selezionato
|
|
// double _currentZoom = 13.0;
|
|
// List<Vineyard> _vineyardList = [];
|
|
// List<Map<int, Coordinates>> _areas = [];
|
|
|
|
// @override
|
|
// void initState() {
|
|
// super.initState();
|
|
// _vineyardList = Get.find<AuthProvider>().profile?.owners?.expand((owner) => owner.vineyards ?? <Vineyard>[]).cast<Vineyard>().toList() ?? [];
|
|
// _areas = _vineyardList
|
|
// .map((vineyard) => vineyard.area)
|
|
// .whereType<Map<int, Coordinates>>()
|
|
// .toList();
|
|
// }
|
|
|
|
// void _zoomIn() {
|
|
// setState(() {
|
|
// _currentZoom += 1;
|
|
// _mapController.move(_mapController.camera.center, _currentZoom);
|
|
// });
|
|
// }
|
|
|
|
// void _zoomOut() {
|
|
// setState(() {
|
|
// _currentZoom -= 1;
|
|
// _mapController.move(_mapController.camera.center, _currentZoom);
|
|
// });
|
|
|
|
// void _fitMapToPolygons(List<Map<int, Coordinates>> areas) {
|
|
// if (areas.isEmpty) {
|
|
// // Se non ci sono aree, centra sulla posizione predefinita
|
|
// _mapController.move(const LatLng(45.4642, 9.1900), 10.0);
|
|
// return;
|
|
// }
|
|
|
|
// // Raccogli tutti i punti da tutte le aree
|
|
// List<LatLng> allPoints = [];
|
|
// for (var areaMap in areas) {
|
|
// for (var coord in areaMap.values) {
|
|
// allPoints.add(LatLng(coord.latitude, coord.longitude));
|
|
// }
|
|
// }
|
|
|
|
// if (allPoints.isEmpty) {
|
|
// // Se per qualche motivo non ci sono punti validi, torna al default
|
|
// _mapController.move(const LatLng(45.4642, 9.1900), 10.0);
|
|
// return;
|
|
// }
|
|
|
|
// // Calcola i limiti (min/max latitudine e longitudine)
|
|
// double? minLat, maxLat, minLon, maxLon;
|
|
|
|
// for (var point in allPoints) {
|
|
// if (minLat == null || point.latitude < minLat) minLat = point.latitude;
|
|
// if (maxLat == null || point.latitude > maxLat) maxLat = point.latitude;
|
|
// if (minLon == null || point.longitude < minLon) minLon = point.longitude;
|
|
// if (maxLon == null || point.longitude > maxLon) maxLon = point.longitude;
|
|
// }
|
|
|
|
// if (minLat != null && maxLat != null && minLon != null && maxLon != null) {
|
|
// // Crea un LatLngBounds
|
|
// final LatLngBounds bounds = LatLngBounds(
|
|
// LatLng(minLat, minLon),
|
|
// LatLng(maxLat, maxLon),
|
|
// );
|
|
|
|
// // Adatta la camera per mostrare tutti i punti
|
|
// _mapController.fitCamera(
|
|
// CameraFit.bounds(
|
|
// bounds: bounds,
|
|
// padding: const EdgeInsets.all(50.0), // Aggiungi un po' di padding
|
|
// ),
|
|
// );
|
|
// // Aggiorna lo zoom corrente dopo il fit
|
|
// _currentZoom = _mapController.camera.zoom;
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// @override
|
|
// Widget build(BuildContext context) {
|
|
// // Puoi usare Obx per reagire ai cambiamenti in AuthProvider se il profilo può cambiare dinamicamente
|
|
// print( _areas);
|
|
// return CardWidget(
|
|
// height: 700,
|
|
// child: Column(
|
|
// children: [
|
|
// Row(
|
|
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
// children: [
|
|
// Text(
|
|
// 'Vigne',
|
|
// style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.green[800]),
|
|
// ),
|
|
// DropdownButton<String>(
|
|
// value: _selectedMapType,
|
|
// items: [
|
|
// DropdownMenuItem(value: 'Standard', child: const Text('Standard')),
|
|
// DropdownMenuItem(value: 'Terreno (OpenTopoMap)', child: const Text('Terreno')),
|
|
// DropdownMenuItem(value: 'Satellite (ESRI)', child: const Text('Satellite')),
|
|
// ],
|
|
// onChanged: (String? newValue) {
|
|
// // Chiamata a setState() per aggiornare l'UI
|
|
// setState(() {
|
|
// _selectedMapType = newValue; // newValue può essere null, ma il DropdownButton lo gestisce
|
|
// });
|
|
// },
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// const SizedBox(height: 16),
|
|
// Expanded(
|
|
// child: Container(
|
|
// // Rimuovi height: 500 se Expanded è sufficiente per il layout
|
|
// decoration: BoxDecoration(
|
|
// border: Border.all(color: Colors.grey),
|
|
// borderRadius: BorderRadius.circular(8.0),
|
|
// ),
|
|
// child: FlutterMap(
|
|
// mapController: _mapController,
|
|
// options: MapOptions(
|
|
// initialCenter:initialMapCenter, // Centro iniziale (es. Milano)
|
|
// initialZoom: 10.0,
|
|
// ),
|
|
// children: [
|
|
// TileLayer(
|
|
// // Usa l'operatore '!' perché _selectedMapType è garantito non essere null qui
|
|
// // dato che ha un valore iniziale e il DropdownButton non permette null.
|
|
// urlTemplate: _mapUrlTemplates[_selectedMapType]!,
|
|
// userAgentPackageName: 'com.example.vigna_app',
|
|
// ),
|
|
// RichAttributionWidget(
|
|
// attributions: [
|
|
// TextSourceAttribution(
|
|
// (_mapAttributions[_selectedMapType]! as Text).data!,
|
|
// onTap: () => print('Attribution tapped: ${(_mapAttributions[_selectedMapType]! as Text).data}'),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// // MarkerLayer per i vigneti
|
|
// MarkerLayer(
|
|
// markers: _vineyardList.map((vineyard) {
|
|
// return Marker(
|
|
// width: 40.0,
|
|
// height: 40.0,
|
|
// point: vineyard.coordinates != null
|
|
// ? LatLng(vineyard.coordinates!.latitude, vineyard.coordinates!.longitude)
|
|
// : const LatLng(0, 0),
|
|
// child: Tooltip( // Aggiunto Tooltip per mostrare il nome del vigneto al tap
|
|
// message: vineyard.name,
|
|
// child: const Icon(Icons.grass, color: Colors.green, size: 30.0),
|
|
// ),
|
|
// );
|
|
// }).toList(),
|
|
// ),
|
|
// Positioned(
|
|
// top: 40,
|
|
// right: 10,
|
|
// child: Column(
|
|
// children: [
|
|
// FloatingActionButton(
|
|
// heroTag: 'zoomIn',
|
|
// mini: true,
|
|
// onPressed: _zoomIn,
|
|
// child: Icon(Icons.add),
|
|
// ),
|
|
// SizedBox(height: 8),
|
|
// FloatingActionButton(
|
|
// heroTag: 'zoomOut',
|
|
// mini: true,
|
|
// onPressed: _zoomOut,
|
|
// child: Icon(Icons.remove),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// if (_areas.isNotEmpty) // Mostra il layer solo se ci sono aree da disegnare
|
|
// PolygonLayer(
|
|
// polygons: _areas.expand((areaMap) {
|
|
// // Converte la Map<int, Coordinates> in List<LatLng>
|
|
// final List<LatLng> polygonPoints = areaMap.values
|
|
// .map((coord) => LatLng(coord.latitude, coord.longitude))
|
|
// .toList();
|
|
|
|
// // FlutterMap PolygonLayer richiede che il poligono sia chiuso.
|
|
// // Ovvero, il primo e l'ultimo punto devono essere uguali.
|
|
// // Aggiungiamo il primo punto alla fine se non è già così.
|
|
// if (polygonPoints.isNotEmpty && polygonPoints.first != polygonPoints.last) {
|
|
// polygonPoints.add(polygonPoints.first);
|
|
// }
|
|
|
|
// // Restituisci una lista di Polygon, uno per ogni area
|
|
// return [
|
|
// if (polygonPoints.length >= 3) // Un poligono valido ha almeno 3 punti
|
|
// Polygon(
|
|
// points: polygonPoints,
|
|
// color: Colors.blue.withOpacity(0.3), // Colore di riempimento dell'area
|
|
// borderStrokeWidth: 2, // Spessore del bordo
|
|
// borderColor: Colors.blue[900]!, // Colore del bordo
|
|
// ),
|
|
// ];
|
|
// }).toList(),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// // Questa SizedBox era posizionata male, l'ho rimossa o spostata se necessaria altrove.
|
|
// // Se volevi spazio alla destra della mappa, dovrebbe essere nel Row con Expanded.
|
|
// // const SizedBox(width: 16), // Rimosso o riposizionato se necessario
|
|
// ],
|
|
// ),
|
|
// );
|
|
// }
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class VignaMapWidget extends StatefulWidget {
|
|
final List<LatLng> polygonPoints;
|
|
final MapTapCallback onMapTap;
|
|
final VoidCallback onClearPolygon;
|
|
final double calculatedArea;
|
|
|
|
const VignaMapWidget({
|
|
super.key,
|
|
required this.polygonPoints,
|
|
required this.onMapTap,
|
|
required this.onClearPolygon,
|
|
required this.calculatedArea,
|
|
});
|
|
|
|
@override
|
|
State<VignaMapWidget> createState() => _VignaMapWidgetState();
|
|
}
|
|
|
|
class _VignaMapWidgetState extends State<VignaMapWidget> {
|
|
final MapController _mapController = MapController();
|
|
|
|
String _selectedMapType = 'Standard'; // Default: OpenStreetMap Standard
|
|
|
|
final Map<String, String> _mapUrlTemplates = {
|
|
'Standard': 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
'Terreno (OpenTopoMap)': 'https://opentopomap.org/{z}/{x}/{y}.png',
|
|
'Satellite (ESRI)': 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
|
};
|
|
|
|
final Map<String, Widget> _mapAttributions = {
|
|
'Standard': const Text('© OpenStreetMap contributors'),
|
|
'Terreno (OpenTopoMap)': const Text('Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © OpenTopoMap (CC-BY-SA)'),
|
|
'Satellite (ESRI)': const Text('Tiles © Esri — Source: Esri, i-cubed, USDA FSA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'),
|
|
};
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return CardWidget(
|
|
height: 700,
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
'Mappa Vigna (Disegna Poligono)',
|
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.green[800]),
|
|
),
|
|
// Dropdown per cambiare il tipo di mappa
|
|
DropdownButtonHideUnderline(
|
|
child: DropdownButton<String>(
|
|
value: _selectedMapType,
|
|
dropdownColor: Colors.green[700],
|
|
style: const TextStyle(color: Colors.white, fontSize: 16),
|
|
icon: const Icon(Icons.map, color: Colors.green), // Icona qui, non nell'AppBar
|
|
items: _mapUrlTemplates.keys.map((String value) {
|
|
return DropdownMenuItem<String>(
|
|
value: value,
|
|
child: Text(value),
|
|
);
|
|
}).toList(),
|
|
onChanged: (String? newValue) {
|
|
setState(() {
|
|
_selectedMapType = newValue!;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
const SizedBox(height: 32),
|
|
Container(
|
|
height: 300,
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: Colors.grey),
|
|
borderRadius: BorderRadius.circular(8.0),
|
|
),
|
|
child: FlutterMap(
|
|
mapController: _mapController,
|
|
options: MapOptions(
|
|
initialCenter: const LatLng(45.4642, 9.1900), // Centro iniziale (es. Milano)
|
|
initialZoom: 10.0,
|
|
onTap: widget.onMapTap, // Usa la callback passata
|
|
),
|
|
children: [
|
|
TileLayer(
|
|
urlTemplate: _mapUrlTemplates[_selectedMapType]!,
|
|
userAgentPackageName: 'com.example.vigna_app',
|
|
),
|
|
RichAttributionWidget(
|
|
attributions: [
|
|
TextSourceAttribution(
|
|
(_mapAttributions[_selectedMapType]! as Text).data!,
|
|
onTap: () => print('Attribution tapped: ${(_mapAttributions[_selectedMapType]! as Text).data}'),
|
|
),
|
|
],
|
|
),
|
|
if (widget.polygonPoints.isNotEmpty)
|
|
PolygonLayer(
|
|
polygons: [
|
|
Polygon(
|
|
points: widget.polygonPoints,
|
|
color: Colors.green.withOpacity(0.5),
|
|
borderStrokeWidth: 3,
|
|
borderColor: Colors.green[900]!,
|
|
),
|
|
],
|
|
),
|
|
if (widget.polygonPoints.isNotEmpty)
|
|
MarkerLayer(
|
|
markers: widget.polygonPoints.map((point) {
|
|
return Marker(
|
|
width: 40.0,
|
|
height: 40.0,
|
|
point: point,
|
|
child: const Icon(
|
|
Icons.grass,
|
|
color: Colors.red,
|
|
size: 30.0,
|
|
),
|
|
);
|
|
}).toList(),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 10),
|
|
Align(
|
|
alignment: Alignment.centerRight,
|
|
child: ElevatedButton.icon(
|
|
onPressed: widget.onClearPolygon, // Usa la callback passata
|
|
icon: const Icon(Icons.clear),
|
|
label: const Text('Resetta Poligono'),
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: Colors.red[400],
|
|
foregroundColor: Colors.white,
|
|
),
|
|
),
|
|
),
|
|
if (widget.calculatedArea > 0)
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 10.0),
|
|
child: Text(
|
|
'Area Calcolata: ${widget.calculatedArea.toStringAsFixed(2)} ettari',
|
|
style: TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.bold,
|
|
color: Colors.green[800],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
} |