From 92ae01503994e16702fd74ce6182cf357d3e1d7c Mon Sep 17 00:00:00 2001 From: Sara <sarasdj@stud.ntnu.no> Date: Fri, 9 Feb 2024 13:41:19 +0100 Subject: [PATCH] refactor: initial Flutter project organization --- app/lib/consts.dart | 0 app/lib/main.dart | 210 +---------------------- app/lib/pages_and_more/consts.dart | 4 + app/lib/pages_and_more/default_page.dart | 163 ++++++++++++++++++ app/lib/pages_and_more/marker_data.dart | 38 ++++ 5 files changed, 208 insertions(+), 207 deletions(-) delete mode 100644 app/lib/consts.dart create mode 100644 app/lib/pages_and_more/consts.dart create mode 100644 app/lib/pages_and_more/default_page.dart create mode 100644 app/lib/pages_and_more/marker_data.dart diff --git a/app/lib/consts.dart b/app/lib/consts.dart deleted file mode 100644 index e69de29b..00000000 diff --git a/app/lib/main.dart b/app/lib/main.dart index c564513e..29aedd7f 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,25 +1,11 @@ import 'package:flutter/material.dart'; -import 'package:flutter_map/flutter_map.dart'; -import 'package:latlong2/latlong.dart'; // Import LatLng class from the latlong package -import 'dart:async'; -import 'dart:io'; -import 'dart:convert'; +import 'pages_and_more/default_page.dart'; - -const String port = "8443"; -const String serverURI = "https://127.0.0.1:$port/"; -const String mapEndpoint = "update_map"; -// NB: if http connection fails, run: adb reverse tcp:8443 tcp:8443 -const int fetchInterval = 5; - -// main is the entry point for the application, and starts the App() function void main() { - runApp(const App()); + runApp(MyApp()); } -class App extends StatelessWidget { - const App({super.key}); - +class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return const MaterialApp( @@ -27,193 +13,3 @@ class App extends StatelessWidget { ); } } - -class DefaultPage extends StatefulWidget { - const DefaultPage({Key? key}) : super(key: key); - - @override - _DefaultPageState createState() => _DefaultPageState(); -} - -// MarkerData holds data for the dynamicllay allocated markers -class MarkerData { - final LatLng location; - final double size; - final Color color; - final double radius; - - MarkerData({required this.location, required this.size, required this.color, required this.radius}); -} - -// parseMarkerData parses jsonData into an object of type MakerData -List<MarkerData> parseMarkerData(String jsonString) { - final parsed = json.decode(jsonString); - return List<MarkerData>.from(parsed.map((data) => MarkerData( - location: LatLng(data['latitude'], data['longitude']), - size: data['size'].toDouble(), - color: parseColor(data['color']), - radius: data['radius'].toDouble(), - ))); -} - -// parseColor parses the color strings into Colors types -Color parseColor(String colorString) { - switch (colorString) { - case 'blue': - return Colors.blue; - case 'red': - return Colors.red; - case 'green': - return Colors.green; - default: - return Colors.black; // Default color if unrecognized - } -} - -class _DefaultPageState extends State<DefaultPage> { - late Timer _timer; - - List<MarkerData> markerList = []; - - // fetchMarkerData requests data from the update_map endpoint - Future<void> fetchMarkerData() async { - try { - // Custom HTTP client - HttpClient client = HttpClient() - ..badCertificateCallback = // NB: temporary disable SSL certificate validation - (X509Certificate cert, String host, int port) => true; - - var request = await client.getUrl(Uri.parse(serverURI+mapEndpoint)); - var response = await request.close(); - - // Parse json response to list of MarkerData objects if request is ok - if (response.statusCode == 200) { - var responseBody = await response.transform(utf8.decoder).join(); - setState(() { - markerList = parseMarkerData(responseBody); - }); - } else { - print('Request failed with status: ${response.statusCode}'); - } - } catch (e) { - print('Failed to connect to the server: $e'); - } - } - - // Timer initializer - @override - void initState() { - super.initState(); - // Call fetchMarkerData when the widget is first created - fetchMarkerData(); - - // Schedule fetchMarkerData to run periodically based on fetchInterval const - const Duration fiveMinutes = Duration(minutes: fetchInterval); - _timer = Timer.periodic(fiveMinutes, (timer) { - fetchMarkerData(); - }); - } - - // Fetch timer - @override - void dispose() { - // Cancel timer on widget termination - _timer.cancel(); - super.dispose(); - } - - // Main widget - @override - Widget build(BuildContext context) { - double screenWidth = MediaQuery.of(context).size.width; - double boxWidth = 0.9; - double boxHeight = 1.5; - const double markerSize = 20; - - return Scaffold( - appBar: AppBar( - title: const Text('IceMap'), - ), - body: Center( - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: <Widget>[ - Container( // Map container - width: screenWidth * boxWidth, - height: screenWidth * boxHeight, - color: Colors.blue, - child: FlutterMap( - options: MapOptions( - center: LatLng(60.7666, 10.8471), - zoom: 9.0, - ), - children: [ - TileLayer( - urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - subdomains: const ['a', 'b', 'c'], - ), - MarkerLayer( - // Dynamically allocate markers based on a list - markers: markerList.map((MarkerData markerData) { - return Marker( - width: markerData.size, - height: markerData.size, - point: markerData.location, - builder: (ctx) => GestureDetector( - onTap: () { - // NB: temporary print - print('Icon tapped'); - // NB: trigger function to add contents to the next box - }, - child: Image.asset( - 'assets/icons/circle-red.png', // Path to your custom icon asset - color: markerData.color, - width: markerData.radius, - height: markerData.radius, - ), - ), - ); - }).toList(), - ), - /*PolygonLayer( - polygons: [ - Polygon( - points: [ - LatLng(60.7600, 10.8000), - LatLng(60.7600, 11.0000), - LatLng(60.7000, 11.0000), - LatLng(60.7000, 10.8000), - ], - color: Colors.blue, - isFilled: true, - ), - ], - ),*/ - ], - ), - ), - const SizedBox(height: 20), - Container( // Detailed info container - width: screenWidth * boxWidth, - height: screenWidth * boxHeight, - color: Colors.blue, - child: const Align( - alignment: Alignment.topLeft, - child: Padding( - padding: EdgeInsets.only(top: 10, left: 10), // Edge padding, text - child: Text( - 'Placeholder text', - style: TextStyle(fontSize: 20, color: Colors.black), - ), - ), - ), - ), - const SizedBox(height: 20), - ], - ), - ), - ), - ); - } -} diff --git a/app/lib/pages_and_more/consts.dart b/app/lib/pages_and_more/consts.dart new file mode 100644 index 00000000..8ee35bb3 --- /dev/null +++ b/app/lib/pages_and_more/consts.dart @@ -0,0 +1,4 @@ +const String port = "8443"; +const String serverURI = "https://127.0.0.1:$port/"; +const String mapEndpoint = "update_map"; +const int fetchInterval = 5; \ No newline at end of file diff --git a/app/lib/pages_and_more/default_page.dart b/app/lib/pages_and_more/default_page.dart new file mode 100644 index 00000000..1b0574c3 --- /dev/null +++ b/app/lib/pages_and_more/default_page.dart @@ -0,0 +1,163 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; +import 'marker_data.dart'; +import 'consts.dart'; + +class DefaultPage extends StatefulWidget { + const DefaultPage({super.key}); + + @override + _DefaultPageState createState() => _DefaultPageState(); +} + +class _DefaultPageState extends State<DefaultPage> { + late Timer _timer; + + List<MarkerData> markerList = []; + + // fetchMarkerData requests data from the update_map endpoint + Future<void> fetchMarkerData() async { + try { + // Custom HTTP client + HttpClient client = HttpClient() + ..badCertificateCallback = // NB: temporary disable SSL certificate validation + (X509Certificate cert, String host, int port) => true; + + var request = await client.getUrl(Uri.parse(serverURI+mapEndpoint)); + var response = await request.close(); + + // Parse json response to list of MarkerData objects if request is ok + if (response.statusCode == 200) { + var responseBody = await response.transform(utf8.decoder).join(); + setState(() { + markerList = parseMarkerData(responseBody); + }); + } else { + print('Request failed with status: ${response.statusCode}'); + } + } catch (e) { + print('Failed to connect to the server: $e'); + } + } + + // Timer initializer + @override + void initState() { + super.initState(); + // Call fetchMarkerData when the widget is first created + fetchMarkerData(); + + // Schedule fetchMarkerData to run periodically based on fetchInterval const + const Duration fiveMinutes = Duration(minutes: fetchInterval); + _timer = Timer.periodic(fiveMinutes, (timer) { + fetchMarkerData(); + }); + } + + // Fetch timer + @override + void dispose() { + // Cancel timer on widget termination + _timer.cancel(); + super.dispose(); + } + + // Main widget + @override + Widget build(BuildContext context) { + double screenWidth = MediaQuery.of(context).size.width; + double boxWidth = 0.9; + double boxHeight = 1.5; + const double markerSize = 20; + + return Scaffold( + appBar: AppBar( + title: const Text('IceMap'), + ), + body: Center( + child: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: <Widget>[ + Container( // Map container + width: screenWidth * boxWidth, + height: screenWidth * boxHeight, + color: Colors.blue, + child: FlutterMap( + options: MapOptions( + center: LatLng(60.7666, 10.8471), + zoom: 9.0, + ), + children: [ + TileLayer( + urlTemplate: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + subdomains: const ['a', 'b', 'c'], + ), + MarkerLayer( + // Dynamically allocate markers based on a list + markers: markerList.map((MarkerData markerData) { + return Marker( + width: markerData.size, + height: markerData.size, + point: markerData.location, + builder: (ctx) => GestureDetector( + onTap: () { + // NB: temporary print + print('Icon tapped'); + // NB: trigger function to add contents to the next box + }, + child: Image.asset( + 'assets/icons/circle-red.png', // Path to your custom icon asset + color: markerData.color, + width: markerData.radius, + height: markerData.radius, + ), + ), + ); + }).toList(), + ), + /*PolygonLayer( + polygons: [ + Polygon( + points: [ + LatLng(60.7600, 10.8000), + LatLng(60.7600, 11.0000), + LatLng(60.7000, 11.0000), + LatLng(60.7000, 10.8000), + ], + color: Colors.blue, + isFilled: true, + ), + ], + ),*/ + ], + ), + ), + const SizedBox(height: 20), + Container( // Detailed info container + width: screenWidth * boxWidth, + height: screenWidth * boxHeight, + color: Colors.blue, + child: const Align( + alignment: Alignment.topLeft, + child: Padding( + padding: EdgeInsets.only(top: 10, left: 10), // Edge padding, text + child: Text( + 'Placeholder text', + style: TextStyle(fontSize: 20, color: Colors.black), + ), + ), + ), + ), + const SizedBox(height: 20), + ], + ), + ), + ), + ); + } +} diff --git a/app/lib/pages_and_more/marker_data.dart b/app/lib/pages_and_more/marker_data.dart new file mode 100644 index 00000000..66b0f252 --- /dev/null +++ b/app/lib/pages_and_more/marker_data.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:latlong2/latlong.dart'; +import 'dart:convert'; + +// MarkerData holds data for the dynamically allocated markers +class MarkerData { + final LatLng location; + final double size; + final Color color; + final double radius; + + MarkerData({required this.location, required this.size, required this.color, required this.radius}); +} + +// parseMarkerData parses jsonData into an object of type MakerData +List<MarkerData> parseMarkerData(String jsonString) { + final parsed = json.decode(jsonString); + return List<MarkerData>.from(parsed.map((data) => MarkerData( + location: LatLng(data['latitude'], data['longitude']), + size: data['size'].toDouble(), + color: parseColor(data['color']), + radius: data['radius'].toDouble(), + ))); +} + +// parseColor parses the color strings into Colors types +Color parseColor(String colorString) { + switch (colorString) { + case 'blue': + return Colors.blue; + case 'red': + return Colors.red; + case 'green': + return Colors.green; + default: + return Colors.black; // Default color if unrecognized + } +} \ No newline at end of file -- GitLab