import 'package:flutter/material.dart'; import '../marker_handler/marker_data.dart'; import '../consts.dart'; import 'quick_view_chart.dart'; import 'stat_charts.dart'; import 'sat_layer.dart'; import 'package:flutter_map/flutter_map.dart'; import 'cloropleth_map.dart'; import 'dart:typed_data'; import 'package:google_fonts/google_fonts.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// MapContainerWidget is the main widget that contains the map with all /// its layers, polygons and markers. class MapContainerWidget extends StatefulWidget { final List<Measurement> markerList; final Uint8List relation; const MapContainerWidget({Key? key, required this.markerList, required this.relation, }) : super(key: key); @override _MapContainerWidgetState createState() => _MapContainerWidgetState(); } class _MapContainerWidgetState extends State<MapContainerWidget> { Measurement? selectedMarker; // Containing data for selected marker int selectedMarkerIndex = 0; bool isMinimized = true; // Quick view box state tacker bool satLayer = false; // Satellite layer visibility tracker bool isTapped = false; // Button tap state tracker final MapController _mapController = MapController(); // Map controller to re-center map view // recenterMap moves the map back to its initial view void recenterMap() { _mapController.move(mapCenter, 9.0); } // Initialise lastUpdate variable from persistent storage if server fetch fails Future<void> checkAndSetLastUpdate() async { if (lastUpdate == null) { final prefs = await SharedPreferences.getInstance(); final updateString = prefs.getString('lastUpdate'); if (updateString != null && updateString.isNotEmpty) { final updateData = DateTime.parse(updateString); setState(() { lastUpdate = updateData; }); } } } // Tile selection handler void handleSelection(int index) { setState(() { selectedMarkerIndex = index; }); } @override Widget build(BuildContext context) { // Initialise selectedMarker to first element in markerList selectedMarker ??= widget.markerList[selectedMarkerIndex]; checkAndSetLastUpdate(); const double contPadding = 30; // Container padding space return LayoutBuilder( builder: (context, constraints) { double screenWidth = constraints.maxWidth; double boxWidth = 0.86; double boxHeight = 1.4; return Column( children: [ const SizedBox(height: contPadding), /*if (true) NB: add search bar const SearchBar(), const SizedBox(height: contPadding),*/ ClipRRect( borderRadius: BorderRadius.circular(20), child: Stack( // Stack of quick view, map layer, satellite layer, and buttons children: [ /*SizedBox( width: screenWidth * boxWidth, height: screenWidth * boxHeight, child: Stack( children: [ SatLayer(markerList: widget.markerList), // Satellite layer Visibility( visible: satLayer, // Only show layer if satellite button is toggled on child: FlutterMap( options: MapOptions( center: mapCenter, zoom: 9.0, ), mapController: _mapController, children: [ TileLayer( urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", subdomains: const ['a', 'b', 'c'], ), MarkerLayer( markers: widget.markerList.map((Measurement measurement) { return Marker( width: 50, height: 50, point: measurement.center, // Set markers at center of measurement builder: (ctx) => GestureDetector( onTap: () { setState(() { selectedMarker = measurement; }); }, child: Icon( Icons.severe_cold, color: measurement == selectedMarker ? Colors.green : Colors.blue, size: measurement == selectedMarker ? 40.0 : 30.0, ), ), ); }).toList(), ), ], ), ), ], ), ),*/ SizedBox( // Colored box behind map width: screenWidth * boxWidth, height: screenWidth * boxHeight, child: Container( color: const Color(0x883366ff), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 5), Text( // Display time of most recent server fetch 'Last updated at ${lastUpdate != null ? (lastUpdate?.day == DateTime.now().day && lastUpdate?.month == DateTime.now().month && lastUpdate?.year == DateTime.now().year ? '${lastUpdate?.hour}:${lastUpdate?.minute}' : '${lastUpdate?.day}-${lastUpdate?.month}-${lastUpdate?.year}') : ''}', style: GoogleFonts.dmSans( fontSize: 13, color: Colors.black, ), ), ], ), ), ), SizedBox( // Lake map width: screenWidth * boxWidth, height: screenWidth * boxHeight, child: Padding( padding: const EdgeInsets.all(15.0), // Padding around map child: ChoroplethMap( relation: widget.relation, measurements: widget.markerList, onSelectionChanged: handleSelection,), ), ), Positioned( // Quick view box layered over map bottom: 10, right: 10, child: ClipRRect( borderRadius: BorderRadius.circular(10), child: Container( width: (screenWidth * boxWidth) / 2.4, height: isMinimized ? 20 : (screenWidth * boxWidth) / 2.4, color: Colors.blue.withOpacity(0.7), child: Stack( children: [ Visibility( // Graph only visible when box is maximized and a marker is selected visible: !isMinimized && selectedMarker != null, child: Center( child: Padding( padding: const EdgeInsets.only(right: 16.0, top: 17.0), child: SizedBox( width: (screenWidth * boxWidth) / 2.3, height: (screenWidth * boxWidth) / 2.3, child: const QuickViewChart(), // Quick view graph ), ), ), ), Positioned( top: 0, right: 5, child: GestureDetector( onTap: () { setState(() { isMinimized = !isMinimized; // Toggle minimized state }); }, child: Icon(isMinimized ? Icons.arrow_drop_up : Icons.arrow_drop_down), ), ), ], ), ), ), ), Positioned( // Satellite button top: 10, right: 10, child: GestureDetector( onTap: () { setState(() { satLayer = !satLayer; // Toggle satellite layer state on press }); }, child: Container( padding: const EdgeInsets.all(8), decoration: satLayer ? const BoxDecoration( // Add decoration only when pressed shape: BoxShape.circle, color: Colors.blue, ) : null, child: const Icon(Icons.satellite_alt_outlined), ), ), ), Positioned( // Back to center button top: 45, right: 10, child: GestureDetector( onTapDown: (_) { setState(() { recenterMap(); // Reset map view isTapped = true; }); }, onTapUp: (_) { setState(() { isTapped = false; }); }, onTapCancel: () { setState(() { isTapped = false; }); }, child: Container( padding: const EdgeInsets.all(8), decoration: isTapped ? const BoxDecoration( // Add decoration only when pressed shape: BoxShape.circle, color: Colors.blue, ) : null, child: const Icon(Icons.settings_backup_restore), ), ), ), ], ), ), const SizedBox(height: contPadding), // Padding between containers ClipRRect( borderRadius: BorderRadius.circular(20), child: SizedBox( width: screenWidth * boxWidth, height: screenWidth * boxHeight * 1.5, // NB: make dynamic child: Align( alignment: Alignment.topLeft, child: Padding( padding: const EdgeInsets.only(top: 20, left: 20), // Edge padding, text child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Ice stats', style: titleStyle, ), const Divider(), Text( 'Time of measurement', style: subHeadingStyle, ), Text( 'Date ${(selectedMarker?.timeMeasured.day ?? '-')}/${(selectedMarker?.timeMeasured.month ?? '-')}/${(selectedMarker?.timeMeasured.year ?? '-')}', style: regTextStyle, ), Text( 'Time: ${selectedMarker?.timeMeasured.hour}:00', style: regTextStyle, ), const SizedBox(height: contPadding), Text( 'Measuring point: (${selectedMarker?.measurementID}, ${selectedMarker?.measurementID})', style: regTextStyle, ), const SizedBox(height: contPadding), const StatCharts(), ], ), ), ), ), ), ], ); }, ); } }