From f816c70e66fc98066064eb145984220b35212ba6 Mon Sep 17 00:00:00 2001 From: alper-savas <alpersavas1998@gmail.com> Date: Sun, 9 Apr 2023 17:08:17 +0200 Subject: [PATCH] Add documentation and readme file --- README.md | 68 ++++++++++++++++++--- lib/main.dart | 148 +++++++++++++++++++++++++++------------------ macos/Podfile.lock | 28 +++++++++ 3 files changed, 177 insertions(+), 67 deletions(-) create mode 100644 macos/Podfile.lock diff --git a/README.md b/README.md index bbc633c..49c532f 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,66 @@ -# router_app +# Shortest Path Application with Flutter -A new Flutter project. +This is a readme file for Shortest Path Application, a Flutter project that allows user to enter origin and destination points along with undesired weather conditions and time period. This application allows users to see the shortest route and see which dates are suitable for travel according to the selected weather conditions. + +### Build with + +- Flutter SDK ## Getting Started -This project is a starting point for a Flutter application. +These instructions will allow you to get a copy of the project and running on your local machine for development and testing purposes. + +### Prerequisites + +- Flutter SDK installed on your machine. +- An editor; you can use either Visual Code or Android Studio. +- An Android or iOS emulator, or a physical device to run the app on. + +### Installing + +1. Clone the repository to your local machine: + +```bash +git clone git@git.imp.fu-berlin.de:swp-datenverwaltung-navigation-2023/navigation-app.git +``` + +2. Navigate to the project directory: + +```bash +cd /your/path/to/the/local/repository +``` + +3. Install dependencies: + +```bash +flutter pub get +``` + +4. Connect a physical device or launch an emulator to test the app. You can find instructions on how to set up an emulator or connect a physical device on the Flutter website: https://flutter.dev/docs/get-started/install. + +5. Run the app: + +```bash +flutter run +``` + +This will build and launch the app on the connected device or emulator. + +## Libraries, Tools & Packages Used -A few resources to get you started if this is your first Flutter project: +- [Flutter Map Docs](https://docs.fleaflet.dev/) +- [Google Autocomplete Docs](https://developers.google.com/maps/documentation/places/web-service/autocomplete) +- [Material UI](https://docs.flutter.dev/development/ui/widgets/material) +- [Internationalization](https://pub.dev/packages/intl) +- [Geolocator](https://pub.dev/packages/geolocator) +- [HTTP](https://pub.dev/packages/http) +- [Convert](https://pub.dev/packages/convert) +- [Async](https://pub.dev/packages/async) +- [Latitude & Longitude](https://pub.dev/packages/latlng) -- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) +## Acknowledgements -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +- [Flutter Docs](https://docs.flutter.dev/) +- [Color Palette & UI](https://coolors.co/) +- [Flutter Map Examples](https://github.com/fleaflet/flutter_map/tree/master/example/lib/pages) +- [Geolocation and Geocoding](https://www.digitalocean.com/community/tutorials/flutter-geolocator-plugin) diff --git a/lib/main.dart b/lib/main.dart index 51e255c..28db3ab 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,6 @@ import 'package:intl/intl.dart'; import 'package:tuple/tuple.dart'; import 'widgets/returnOrigin.dart'; import 'widgets/returnDestination.dart'; -import 'package:geocoding/geocoding.dart'; import 'package:geolocator/geolocator.dart'; import 'package:http/http.dart' as http; import 'dart:async'; @@ -24,11 +23,9 @@ class MyApp extends StatelessWidget { return MaterialApp( debugShowCheckedModeBanner: false, title: 'Flutter App', - home: MyHomePage(), + home: HomePage(), theme: ThemeData( fontFamily: 'Rubik', - // Color.fromRGBO(23, 26, 32, 1), - // Color.fromRGBO(13, 108, 114, 1) primaryColor: Color.fromRGBO(23, 26, 32, 1), accentColor: Color.fromRGBO(250, 250, 250, 1), ), @@ -36,29 +33,31 @@ class MyApp extends StatelessWidget { } } -// Helper class +/// Helper class to create polylines. class LineString { LineString(this.lineString); List<dynamic> lineString; } -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key}); +class HomePage extends StatefulWidget { + const HomePage({super.key}); @override - State<MyHomePage> createState() => _MyHomePageState(); + State<HomePage> createState() => _HomePageState(); } -class _MyHomePageState extends State<MyHomePage> { +class _HomePageState extends State<HomePage> { Config config = Config(); - // Initial map coords and map controller to control camera movements. + + /// Initial map coordinates and map controller to control camera movements. late final MapController _mapController = MapController(); - // Input field texts for origin/dest points. + /// Input field texts for origin/dest points. String originText = 'Starting Point...'; String destinationText = 'Destination...'; - // Set origin/dest coords, respective markers, lat/lng coords between poits and polyline corresponding to coords. + /// Variable related to origin/destination coordinates, markers, shortest path coordinates, corresponding polyline list, + /// total duration, distance and time period. late latlng.LatLng _originCoordinates; late latlng.LatLng _destCoordinates; int _markerCounter = 0; @@ -69,7 +68,7 @@ class _MyHomePageState extends State<MyHomePage> { String _totalDistance = ''; String _totalDaysFormatted = ''; - // Variables for calendar and weather filter + /// Variables related to filters such as weather and date. DateTimeRange _dateRange = DateTimeRange( start: DateTime.now(), end: DateTime.now(), @@ -80,13 +79,13 @@ class _MyHomePageState extends State<MyHomePage> { var _dateRangeArray = []; var _availableDatesForTrip = []; - // Weather API + /// Variables related to weather API. var _forecastList = []; bool _isCollapsed = false; bool _isLoading = false; - // Format input text. - String getFormattedText(String inputText) { + /// Takes the [inputText] value of [String] type and returns a formatted version of it. + String _getFormattedText(String inputText) { if (inputText != null) { if (inputText.length > 15) { return '${inputText.substring(0, 15)}...'; @@ -95,8 +94,9 @@ class _MyHomePageState extends State<MyHomePage> { return inputText; } - // -------------------Section For Input Pages------------------- - // Wait return value of input page to take origin point and update it's coords. + /// Takes [context] value of [BuildContext] type and awaits address information of [String] and checks if there is + /// a corresponding address. If there is corresponding address, updates origin text and origin coordinates for given + /// address. void _awaitStartingPointReturnValue(BuildContext context) async { final result = await Navigator.push( context, @@ -105,8 +105,6 @@ class _MyHomePageState extends State<MyHomePage> { ), ); if (result != null) { - List<Location> originLoc = await locationFromAddress(result); - //Match adress with Backend final response = await http.post( Uri.parse('http://10.0.2.2:8080/addressmatching'), @@ -114,7 +112,6 @@ class _MyHomePageState extends State<MyHomePage> { headers: <String, String>{"Content-Type": "application/json"}); _data = jsonDecode(response.body); var coordinate = _data["coordinate"]; - print(response.body); if (_data.containsKey('error_code') && _data['error_code'] != 0) { switch (_data['error_code']) { @@ -146,7 +143,9 @@ class _MyHomePageState extends State<MyHomePage> { _resetPolyline(); } - // Wait return value of input page to take dest point and update it's coords. + /// Takes [context] value of [BuildContext] type and awaits address information of [String] and checks if there is + /// a corresponding address. If there is corresponding address, updates destination text and destination coordinates + /// for given address. void _awaitDestinationPointReturnValue(BuildContext context) async { final result = await Navigator.push( context, @@ -155,12 +154,31 @@ class _MyHomePageState extends State<MyHomePage> { ), ); if (result != null) { - List<Location> destLoc = await locationFromAddress(result); + //Match adress with Backend + final response = await http.post( + Uri.parse('http://10.0.2.2:8080/addressmatching'), + body: jsonEncode({"address": result}), + headers: <String, String>{"Content-Type": "application/json"}); + _data = jsonDecode(response.body); + var coordinate = _data["coordinate"]; + + if (_data.containsKey('error_code') && _data['error_code'] != 0) { + switch (_data['error_code']) { + case 1: + _showMessage( + 'The address is outside the solution space. Please enter an address within Berlin-Brandenburg.', + context); + return; + case 2: + _showMessage('The address could not be found.', context); + return; + } + } setState( () { destinationText = result; _destCoordinates = - latlng.LatLng(destLoc[0].latitude, destLoc[0].longitude); + latlng.LatLng(coordinate["lat"], coordinate["lon"]); }, ); } @@ -178,8 +196,7 @@ class _MyHomePageState extends State<MyHomePage> { _resetPolyline(); } - // -----------Section For Map Operations, Coords, Polyline..---------------- - // Get shortest path. + /// Reset already existing polylines and call [_getDirection()] function along with [_getWeather()] function. void _getShortestPath() async { _resetPolyline(); @@ -196,7 +213,7 @@ class _MyHomePageState extends State<MyHomePage> { }); } - // Add markers for origin/dest coords. + /// Creates Marker instance based on origin coordinates and updates [_marker] list. void _addOriginMarker() { _markers.add( Marker( @@ -215,6 +232,7 @@ class _MyHomePageState extends State<MyHomePage> { }); } + /// Creates Marker instance based on destination coordinates and updates [_marker] list. void _addDestinationMarker() { _markers.add( Marker( @@ -233,6 +251,7 @@ class _MyHomePageState extends State<MyHomePage> { }); } + /// Takes string of type [String] and context of type [BuildContext] to show alert dialog on screen void _showMessage(String string, BuildContext context) { showDialog<String>( context: context, @@ -248,7 +267,10 @@ class _MyHomePageState extends State<MyHomePage> { ); } - // Get coords between origin/dest points corresponding to shortest path. + /// Makes post request to get coordinates of shortest path between origin and destination point. After necessary + /// information fetched, updates [_data] variable to store coordinates. Then validates and checks whether the route was + /// found. Updates [duration], [distance] and [daysAfterToday] variables and apply necessary formats. Then calls [drawPolyline] + /// function to draw a polyline for given points, calculates bound and animates the camera depending on bounds. Future<void> _getDirections() async { _resetPolyline(); @@ -316,15 +338,17 @@ class _MyHomePageState extends State<MyHomePage> { } } + /// Takes [coordinates] of [List<LatLng>] type and add [_polyline] variable to store necessary polyline coordinates + /// to render them on screen. void drawPolyline(coordinates) { LineString ls = LineString(coordinates); - // Draw polyline based on given coordinates for (int i = 0; i < ls.lineString.length; i++) { _polyline.add(latlng.LatLng(ls.lineString[i][0], ls.lineString[i][1])); } } + /// Gets [durationInMins] of [int] type and apply formatting to return [String]. String getTotalDuration(int durationInMins) { int hours = (durationInMins / 60).floor(); int min = (durationInMins % 60).floor(); @@ -334,18 +358,21 @@ class _MyHomePageState extends State<MyHomePage> { return '$min mins'; } + /// Gets [distance] of [int] type and apply formatting to return [String]. String getTotalDistance(int distance) { int distanceInKm = (distance / 1000).ceil(); return '$distanceInKm km'; } + /// Gets [daysAfterToday] of [int] type and apply formatting to return [String]. String getDay(int daysAfterToday) { DateTime today = DateTime.now(); - String date = convertDateFormat(today.add(Duration(days: daysAfterToday))); + String date = _convertDateFormat(today.add(Duration(days: daysAfterToday))); print(date); return date; } + /// Updates [forbidden] of [List<String>] type based on undesired weather options and returns it. List<String> getForbidden() { List<String> forbidden = []; for (var option in _selectedOptions) { @@ -367,12 +394,13 @@ class _MyHomePageState extends State<MyHomePage> { return forbidden; } + /// Returns tuple of [int] based on desired date range entered by user. Tuple2<int, int> getDayOffset() { return Tuple2<int, int>(_dateRange.start.difference(DateTime.now()).inDays, _dateRange.end.difference(DateTime.now()).inDays); } - // Helper function to reset polyline before calculating new polyline for another route. + /// Helper function to reset polyline before calculating new polyline for another route. void _resetPolyline() { _polyline = []; setState(() { @@ -380,7 +408,7 @@ class _MyHomePageState extends State<MyHomePage> { }); } - // Helper function to reset marker. + /// Helper function to reset marker. void _resetMarker() { _markers = {}; setState(() { @@ -388,8 +416,9 @@ class _MyHomePageState extends State<MyHomePage> { }); } - // Get user location info with geolocation. - _getCurrentLocation() async { + /// Based on user's current position, makes http request with the help of users latitude and longitude information, + /// updates [originText] and [_originCoordinates] based on these information and locates user with the help of geolocation. + void _getCurrentLocation() async { await Geolocator.requestPermission().then( (value) => { Geolocator.getCurrentPosition( @@ -420,7 +449,7 @@ class _MyHomePageState extends State<MyHomePage> { ); } - // Add origin marker on touch. + /// Creates Marker instance based on users touch and updates [_marker] list. void _appearOriginMarkerOnTouch(latlng.LatLng pos) async { _resetPolyline(); _resetMarker(); @@ -447,7 +476,7 @@ class _MyHomePageState extends State<MyHomePage> { }); } - // Add destination marker on touch. + /// Creates Marker instance based on users touch and updates [_marker] list. void _appearDestMarkerOnTouch(latlng.LatLng pos) async { _resetPolyline(); if (_markers.length > 1) { @@ -477,8 +506,8 @@ class _MyHomePageState extends State<MyHomePage> { }); } -// ----------Section For Filter Functions, Calendar, Weather------------ -// Pick date + /// First navigates user to a different page and let user to choose a time period. After uses choses a time period, then + /// updates_dateRange instance of type [DateTimeRange]. Takes [ctx] of [BuildContext] type. _rangeDatePicker(BuildContext ctx) async { DateTimeRange? newDateTimeRange = await showDateRangePicker( initialEntryMode: DatePickerEntryMode.calendarOnly, @@ -514,7 +543,7 @@ class _MyHomePageState extends State<MyHomePage> { _updateDateRange(); } - // Update Data Range Array + /// Formats [_dateRange] variable and store start and end dates in a seperate list called [_dateRangeArray]. void _updateDateRange() { DateFormat formatter = DateFormat('y-MM-dd'); String formattedStart = formatter.format(_dateRange.start); @@ -523,28 +552,29 @@ class _MyHomePageState extends State<MyHomePage> { _dateRangeArray.add(formattedEnd); } - // Format and display date. - String convertDateFormat(date) { + /// Helper function that takes [date] as argument and returns formatted [String]. + String _convertDateFormat(date) { DateFormat formatter = DateFormat('MM/dd'); String formatted = formatter.format(date); return formatted; } - String displayDate() { + /// Helper function to display date. + String _displayDate() { if (_isDateChosen) { - return "${convertDateFormat(_dateRange.start)} - ${convertDateFormat(_dateRange.end)}"; + return "${_convertDateFormat(_dateRange.start)} - ${_convertDateFormat(_dateRange.end)}"; } return ''; } - // Expand buttons + /// Helper fnuction to toggle additional button for filter such as weather and calendar. void _toggleAdditionalButtons() { setState(() { _showAdditionalButtons = !_showAdditionalButtons; }); } - // Show weather options as modal bottom sheet + /// Display bottom sheet that contains weather options for client to choose undesired weather conditions. void _showOptions(BuildContext context) { showModalBottomSheet( context: context, @@ -607,8 +637,8 @@ class _MyHomePageState extends State<MyHomePage> { ); } - // Display selected weather conditions with icons - List<Widget> returnWidget() { + /// Helper function to create a list of widgets that contains [Text] widget and [Icon] widget together for undesired weather. + List<Widget> _returnWidget() { List<Widget> widgetList = []; for (var item in _selectedOptions) { widgetList.add(Text( @@ -623,7 +653,7 @@ class _MyHomePageState extends State<MyHomePage> { child: Text(' '), )); widgetList.add( - Container(margin: EdgeInsets.only(bottom: 10), child: getIcon(item)), + Container(margin: EdgeInsets.only(bottom: 10), child: _getIcon(item)), ); widgetList.add( Text( @@ -635,8 +665,8 @@ class _MyHomePageState extends State<MyHomePage> { return widgetList.sublist(0, widgetList.length - 1); } - // Helper function to get corresponding icon - Icon getIcon(String iconName) { + /// Helper function to get corresponding icon. Takes [iconName] of [String] type and return corresponding [Icon]. + Icon _getIcon(String iconName) { switch (iconName) { case 'Rain': return Icon( @@ -667,8 +697,9 @@ class _MyHomePageState extends State<MyHomePage> { } } - // ----------Section For Getting Weather Data------------ - // Create weather query to the external weather API + /// Creates weather query to the external weather API and checks weather conditions in destination point. If weather + /// conditions in a particular date contradicts with the undesired weather conditions of user, then exlude that day. Otherwise + /// add that day to a [_availableDatesForTrip] list and update it at the end. Future<void> _getWeather() async { _forecastList = []; setState(() { @@ -735,6 +766,7 @@ class _MyHomePageState extends State<MyHomePage> { }); } + /// Helper function to check if info button should be collapsed or not. void _collapse() { if (_isCollapsed) { _isCollapsed = false; @@ -823,8 +855,8 @@ class _MyHomePageState extends State<MyHomePage> { color: Color.fromRGBO(20, 20, 20, 1), ), border: InputBorder.none, - prefixText: getFormattedText(originText), - labelText: getFormattedText(originText), + prefixText: _getFormattedText(originText), + labelText: _getFormattedText(originText), floatingLabelBehavior: FloatingLabelBehavior.never, ), @@ -890,8 +922,8 @@ class _MyHomePageState extends State<MyHomePage> { color: Color.fromRGBO(20, 20, 20, 1), ), border: InputBorder.none, - prefixText: getFormattedText(destinationText), - labelText: getFormattedText(destinationText), + prefixText: _getFormattedText(destinationText), + labelText: _getFormattedText(destinationText), floatingLabelBehavior: FloatingLabelBehavior.never, ), @@ -1047,7 +1079,7 @@ class _MyHomePageState extends State<MyHomePage> { .accentColor), ), Text( - displayDate(), + _displayDate(), style: TextStyle( fontSize: 20, fontWeight: FontWeight.w400, @@ -1103,7 +1135,7 @@ class _MyHomePageState extends State<MyHomePage> { width: 20, ), if (!_selectedOptions.isEmpty) - ...returnWidget(), + ..._returnWidget(), ], ), ], diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 0000000..0d55c25 --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,28 @@ +PODS: + - FlutterMacOS (1.0.0) + - geolocator_apple (1.2.0): + - FlutterMacOS + - location (0.0.1): + - FlutterMacOS + +DEPENDENCIES: + - FlutterMacOS (from `Flutter/ephemeral`) + - geolocator_apple (from `Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos`) + - location (from `Flutter/ephemeral/.symlinks/plugins/location/macos`) + +EXTERNAL SOURCES: + FlutterMacOS: + :path: Flutter/ephemeral + geolocator_apple: + :path: Flutter/ephemeral/.symlinks/plugins/geolocator_apple/macos + location: + :path: Flutter/ephemeral/.symlinks/plugins/location/macos + +SPEC CHECKSUMS: + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + geolocator_apple: 72a78ae3f3e4ec0db62117bd93e34523f5011d58 + location: 7cdb0665bd6577d382b0a343acdadbcb7f964775 + +PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7 + +COCOAPODS: 1.12.0 -- GitLab