diff --git a/app/lib/api_handler/fetch_markers.dart b/app/lib/api_handler/fetch_markers.dart deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/app/lib/main.dart b/app/lib/main.dart index 697787360328977a997f45dba0a293655ae96c6b..10b3118b6bb06acf9ed72c7e3de427f0e5aad0e8 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'pages_and_widgets/default_page.dart'; +import 'pages/default_page.dart'; void main() { runApp(MyApp()); diff --git a/app/lib/pages_and_widgets/consts.dart b/app/lib/pages/consts.dart similarity index 100% rename from app/lib/pages_and_widgets/consts.dart rename to app/lib/pages/consts.dart diff --git a/app/lib/pages/default_page.dart b/app/lib/pages/default_page.dart new file mode 100644 index 0000000000000000000000000000000000000000..2277909918abc621c369a5f85bbe24d1e367b13f --- /dev/null +++ b/app/lib/pages/default_page.dart @@ -0,0 +1,89 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'widgets/map_widget.dart'; +import 'widgets/details_widget.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<MarkerTemplate> markerList = []; + + // fetchMarkerTemplate requests data from the update_map endpoint + Future<void> fetchMarkerTemplate() async { + try { + // Custom HTTP client + HttpClient client = HttpClient() + ..badCertificateCallback = // NB: temporary disable SSL certificate validation + (X509Certificate cert, String host, int port) => true; + + // Request makers from API and wait for response + var request = await client.getUrl(Uri.parse(serverURI+mapEndpoint)); + var response = await request.close(); + + // Attempt to parse json if request is ok + if (response.statusCode == 200) { + var responseBody = await response.transform(utf8.decoder).join(); + setState(() { + // Parse JSON string from response body + List<dynamic> jsonData = json.decode(responseBody); + + // Convert response from type List<dynamic> to List<MarkerTemplate> + markerList = jsonData.map((data) => MarkerTemplate.fromJson(data)).toList(); + }); + } else { + print('Request failed with status: ${response.statusCode}'); + } + } catch (e) { + print('Failed to connect to the server: $e'); + } + } + + // State initializer + @override + void initState() { + super.initState(); + // Call fetchMarkerTemplate when the widget is first created + fetchMarkerTemplate(); + + // Schedule fetchMarkerTemplate to run periodically based on fetchInterval const + const Duration fiveMinutes = Duration(minutes: fetchInterval); + _timer = Timer.periodic(fiveMinutes, (timer) { + fetchMarkerTemplate(); + }); + } + + // Fetch timer + @override + void dispose() { + // Cancel timer on widget termination + _timer.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('My Page'), + ), + body: ListView( + children: [ + MapContainerWidget(markerList: markerList), + const SizedBox(height: 30), // Padding between widgets + const DetailedInfoContainerWidget(), + ], + ), + ); + } +} \ No newline at end of file diff --git a/app/lib/pages_and_widgets/marker_data.dart b/app/lib/pages/marker_data.dart similarity index 100% rename from app/lib/pages_and_widgets/marker_data.dart rename to app/lib/pages/marker_data.dart diff --git a/app/lib/pages/widgets/details_widget.dart b/app/lib/pages/widgets/details_widget.dart new file mode 100644 index 0000000000000000000000000000000000000000..d42c4d6f87952161dd450eec96a1b51fc2b3ee75 --- /dev/null +++ b/app/lib/pages/widgets/details_widget.dart @@ -0,0 +1,29 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class DetailedInfoContainerWidget extends StatelessWidget { + const DetailedInfoContainerWidget({super.key}); + + @override + Widget build(BuildContext context) { + double screenWidth = MediaQuery.of(context).size.width; + double boxWidth = 0.9; + double boxHeight = 1.5; + + return 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), + ), + ), + ), + ); + } +} \ No newline at end of file diff --git a/app/lib/pages/widgets/map_widget.dart b/app/lib/pages/widgets/map_widget.dart new file mode 100644 index 0000000000000000000000000000000000000000..cf40ce9e0225b9dda7711ad87eba18d3bf6a2f4c --- /dev/null +++ b/app/lib/pages/widgets/map_widget.dart @@ -0,0 +1,77 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import '../marker_data.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:latlong2/latlong.dart'; + +class MapContainerWidget extends StatelessWidget { + final List<MarkerTemplate> markerList; + + const MapContainerWidget({super.key, required this.markerList}); + + @override + Widget build(BuildContext context) { + double screenWidth = MediaQuery + .of(context) + .size + .width; + double boxWidth = 0.9; + double boxHeight = 1.5; + + return 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( + markers: markerList.map((MarkerTemplate markerTemplate) { + return Marker( + width: markerTemplate.size, + height: markerTemplate.size, + point: markerTemplate.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: markerTemplate.color, + width: markerTemplate.size, + height: markerTemplate.size, + ), + ), + ); + }).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, + ), + ], + ),*/ + ], + ), + ); + } +} \ No newline at end of file diff --git a/app/lib/pages_and_widgets/default_page.dart b/app/lib/pages_and_widgets/default_page.dart deleted file mode 100644 index 15471ac025d51edc84f1754ebabab91e557f895e..0000000000000000000000000000000000000000 --- a/app/lib/pages_and_widgets/default_page.dart +++ /dev/null @@ -1,188 +0,0 @@ -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<MarkerTemplate> markerList = []; - - // fetchMarkerTemplate requests data from the update_map endpoint - Future<void> fetchMarkerTemplate() async { - try { - // Custom HTTP client - HttpClient client = HttpClient() - ..badCertificateCallback = // NB: temporary disable SSL certificate validation - (X509Certificate cert, String host, int port) => true; - - // Request makers from API and wait for response - var request = await client.getUrl(Uri.parse(serverURI+mapEndpoint)); - var response = await request.close(); - - // Attempt to parse json if request is ok - if (response.statusCode == 200) { - var responseBody = await response.transform(utf8.decoder).join(); - setState(() { - // Parse JSON string from response body - List<dynamic> jsonData = json.decode(responseBody); - - // Convert response from type List<dynamic> to List<MarkerTemplate> - markerList = jsonData.map((data) => MarkerTemplate.fromJson(data)).toList(); - }); - } else { - print('Request failed with status: ${response.statusCode}'); - } - } catch (e) { - print('Failed to connect to the server: $e'); - } - } - - // State initializer - @override - void initState() { - super.initState(); - // Call fetchMarkerTemplate when the widget is first created - fetchMarkerTemplate(); - - // Schedule fetchMarkerTemplate to run periodically based on fetchInterval const - const Duration fiveMinutes = Duration(minutes: fetchInterval); - _timer = Timer.periodic(fiveMinutes, (timer) { - fetchMarkerTemplate(); - }); - } - - // Fetch timer - @override - void dispose() { - // Cancel timer on widget termination - _timer.cancel(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('My Page'), - ), - body: ListView( - children: [ - MapContainerWidget(markerList: markerList), - const SizedBox(height: 30), // Padding between widgets - DetailedInfoContainerWidget(), - ], - ), - ); - } -} - -class MapContainerWidget extends StatelessWidget { - final List<MarkerTemplate> markerList; - - const MapContainerWidget({super.key, required this.markerList}); - - @override - Widget build(BuildContext context) { - double screenWidth = MediaQuery - .of(context) - .size - .width; - double boxWidth = 0.9; - double boxHeight = 1.5; - - return 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( - markers: markerList.map((MarkerTemplate markerTemplate) { - return Marker( - width: markerTemplate.size, - height: markerTemplate.size, - point: markerTemplate.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: markerTemplate.color, - width: markerTemplate.size, - height: markerTemplate.size, - ), - ), - ); - }).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, - ), - ], - ),*/ - ], - ), - ); - } -} - -class DetailedInfoContainerWidget extends StatelessWidget { - const DetailedInfoContainerWidget({super.key}); - - @override - Widget build(BuildContext context) { - double screenWidth = MediaQuery.of(context).size.width; - double boxWidth = 0.9; - double boxHeight = 1.5; - - return 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), - ), - ), - ), - ); - } - } \ No newline at end of file