Newer
Older
import 'package:flutter_map/flutter_map.dart';
import 'package:fuzzy/fuzzy.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'stat_charts.dart';
import '../../consts.dart';
import 'choropleth_map.dart';
import '../data_classes.dart';
import 'satellite_layer.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;
const MapContainerWidget({Key? key,
required this.markerList,
required this.serverConnection,
@override
_MapContainerWidgetState createState() => _MapContainerWidgetState();
}
class _MapContainerWidgetState extends State<MapContainerWidget> {
Measurement? selectedTile; // Containing data for selected marker
bool isMinimized = true; // Quick view box state tacker
bool satLayer = false; // Satellite layer visibility tracker
bool OSMlayer = false;
bool isSatTapped = false; // Button tap state tracker, satellite
bool isMapTapped = false; // Button tap state tracker, OSmap
// 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(() {
// NB should be optimalised
for (Measurement measurement in widget.markerList) {
for (SubDiv subdivision in measurement.subDivs) {
if (subdivision.sub_div_id == indexString) {
selectedTile= widget.markerList[index];
break;
}
}
}
});
}
@override
Widget build(BuildContext context) {
// Initialise selectedMarker to first element in markerList
selectedTile ??= widget.markerList[0];
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*1.5),
ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Stack( // Stack of quick view, map layer, satellite layer, and buttons
SizedBox( // Colored box behind map
width: screenWidth * boxWidth,
height: screenWidth * boxHeight,
child: Container(
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,),
SizedBox(
width: screenWidth * boxWidth,
height: screenWidth * boxHeight,
child: Visibility(
visible: OSMlayer,
child: OSM(markerList: widget.markerList),
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,
child: const Icon(
Icons.satellite_alt_outlined,
color: Colors.white54,
),
Positioned( // OSmap button
top: 50,
right: 10,
child: GestureDetector(
onTap: () {
setState(() {
OSMlayer = !OSMlayer; // Toggle satellite layer state on press
});
},
child: Container(
padding: const EdgeInsets.all(8),
decoration: OSMlayer ? const BoxDecoration( // Add decoration only when pressed
shape: BoxShape.circle,
color: Colors.grey,
) : null,
child: const Icon(
Icons.map,
color: Colors.white54,
),
),
),
),
Positioned( // No wifi icon
top: 80,
right: 10,
child: GestureDetector(
onTapDown: (_) {
setState(() {
// Add functionality
});
},
onTapUp: (_) {
setState(() {
// Add functionality
});
},
onTapCancel: () {
setState(() {
// Add functionality
});
},
child: Visibility( // Show icon only when no server connection
visible: !widget.serverConnection,
child: Container(
padding: const EdgeInsets.all(8),
decoration: isSatTapped ? const BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
) : null,
child: const Icon(Icons.perm_scan_wifi, color: Color(0xFF5B0000)),
),
),
),
),
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}.${formatMonth(lastUpdate!.month)} ${lastUpdate?.year}') : ''}',
style: GoogleFonts.dmSans(
fontSize: 14,
color: Colors.white60,
),
),
],
),
const SizedBox(height: contPadding), // Padding between containers
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: screenWidth * boxWidth,
child: Align(
alignment: Alignment.topLeft,
child: Padding(
padding: const EdgeInsets.only(top: 20, left: 30), // Updated padding
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Ice stats',
style: titleStyle,
),
const Divider(),
const SizedBox(height: 10), // Reduced padding
Text(
'Measured at ',
style: subHeadingStyle,
),
Text(
'Date: ${(selectedTile?.timeMeasured.day ?? '-')}/${(selectedTile?.timeMeasured.month ?? '-')}/${(selectedTile?.timeMeasured.year ?? '-')}',
style: regTextStyle,
),
Text(
'Time: ${selectedTile?.timeMeasured.hour}:00',
style: regTextStyle,
),
],
),
),
),
),
const SizedBox(height: contPadding*2),
SizedBox(
width: screenWidth * boxWidth * 1.2,
child: Center(
child: Text(
'Measuring point: (${selectedTile?.measurementID}, ${selectedTile?.measurementID})',
style: regTextStyle,
),
),
),
SizedBox(
width: screenWidth * boxWidth * 1.2,
child: const StatCharts(),
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
),
const SizedBox(height: contPadding*2),
SizedBox(
width: screenWidth * boxWidth * 1.2,
child: Padding(
padding: const EdgeInsets.only(top: 20, left: 30),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Icon(
Icons.info,
color: Colors.white54,
),
const SizedBox(width: 10),
Expanded(
child: Text(
'For every x of y, there has to be z cm of '
'q for every kg of applied weight to ensure ?',
style: regTextStyle,
),
),
],
),
],
),
),
),
const SizedBox(height: contPadding*2),
],
);
},
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
String formatMonth(int month) {
switch (month) {
case 1:
return 'Jan';
case 2:
return 'Feb';
case 3:
return 'Mar';
case 4:
return 'Apr';
case 5:
return 'May';
case 6:
return 'Jun';
case 7:
return 'Jul';
case 8:
return 'Aug';
case 9:
return 'Sep';
case 10:
return 'Oct';
case 11:
return 'Nov';
case 12:
return 'Dec';
default:
return '';
}
}