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),
if (widget.showSearchBar) // NB temp always true
_SearchBar(),
const SizedBox(height: contPadding),
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}-${lastUpdate?.month}-${lastUpdate?.year}') : ''}',
style: GoogleFonts.dmSans(
fontSize: 14,
color: Colors.white60,
),
),
],
),
const SizedBox(height: contPadding), // Padding between containers
ClipRRect(
borderRadius: BorderRadius.circular(20),
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: [
style: titleStyle,
'Date: ${(selectedTile?.timeMeasured.day ?? '-')}/${(selectedTile?.timeMeasured.month ?? '-')}/${(selectedTile?.timeMeasured.year ?? '-')}',
'Time: ${selectedTile?.timeMeasured.hour}:00',
style: regTextStyle,
),
const SizedBox(height: contPadding),
'Measuring point: (${selectedTile?.measurementID}, ${selectedTile?.measurementID})',
const SizedBox(height: contPadding),
const StatCharts(),
],
),
),
),
],
);
},
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
}
class _SearchBar extends StatelessWidget {
final width = 350.0; // NB pass value to class
final List<String> testSearchNames = [
"Mjøsa",
"Bogstadsvannet",
"Einavatnet",
"Femsjøen",
"Femunden",
"Fjellsjøen",
"Gjende",
"Gjersjøen"
];
// Searching function for lake names using Fuzzy library
List<String> searchLakeNames(String query) {
final options = FuzzyOptions(threshold: 0.3, findAllMatches: true);
final matcher = Fuzzy(testSearchNames, options: options);
final results = matcher.search(query);
// Extracting lake names from the results and casting them to strings
return results.map((result) => result.item as String).toList();
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: SizedBox(
height: 35, // Adjust the height here
width: width,
child: TextField(
decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
hintText: 'Search...',
filled: true,
fillColor: Colors.white,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15.0),
),
),
onChanged: (value) {
searchLakeNames(value);
},
),
),
);
}