diff --git a/app.apk b/app.apk index f85bdb5..76fee3c 100644 Binary files a/app.apk and b/app.apk differ diff --git a/client/android/app/build.gradle b/client/android/app/build.gradle index e7767d9..aeffb6a 100644 --- a/client/android/app/build.gradle +++ b/client/android/app/build.gradle @@ -44,7 +44,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.temkaatranshprojects.client" - minSdkVersion flutter.minSdkVersion + minSdkVersion 20 targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/client/android/app/src/main/AndroidManifest.xml b/client/android/app/src/main/AndroidManifest.xml index 278d75e..a1905e5 100644 --- a/client/android/app/src/main/AndroidManifest.xml +++ b/client/android/app/src/main/AndroidManifest.xml @@ -33,5 +33,7 @@ + diff --git a/client/android/app/src/main/kotlin/com/temkaatranshprojects/client/MainActivity.kt b/client/android/app/src/main/kotlin/com/temkaatranshprojects/client/MainActivity.kt index 5f24228..33bdcf7 100644 --- a/client/android/app/src/main/kotlin/com/temkaatranshprojects/client/MainActivity.kt +++ b/client/android/app/src/main/kotlin/com/temkaatranshprojects/client/MainActivity.kt @@ -3,4 +3,5 @@ package com.temkaatranshprojects.client import io.flutter.embedding.android.FlutterActivity class MainActivity: FlutterActivity() { + } diff --git a/client/lib/main.dart b/client/lib/main.dart index e942fbe..ffa3ce3 100644 --- a/client/lib/main.dart +++ b/client/lib/main.dart @@ -36,8 +36,9 @@ class App extends StatelessWidget { // 15. add other cities rates (done) // 16. change app name (done) // 17. make russian application (done) -// 18. update readme (done_ +// 18. update readme (done) // 19. fix buttons (done) +// 20. add google map (done) // flutter build apk --release // build/app/outputs/flutter-apk/app-release.apk diff --git a/client/lib/ui/app_bar/app_bar.dart b/client/lib/ui/app_bar/app_bar.dart index b4a9992..9dcbd48 100644 --- a/client/lib/ui/app_bar/app_bar.dart +++ b/client/lib/ui/app_bar/app_bar.dart @@ -23,6 +23,11 @@ class DepartmentsAppBar extends StatelessWidget with PreferredSizeWidget { width: 30, height: 35, child: Icon(Icons.settings) + ), + SizedBox( + width: 30, + height: 35, + child: Icon(Icons.location_city) ) ], ), diff --git a/client/lib/ui/body/map.dart b/client/lib/ui/body/map.dart new file mode 100644 index 0000000..0500cf5 --- /dev/null +++ b/client/lib/ui/body/map.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/gestures.dart'; + +class GoogleMapTab extends StatefulWidget { + final LatLng userPosition; + final List> departmentsInfo; + + const GoogleMapTab({ + Key? key, + required LatLng this.userPosition, + required List> this.departmentsInfo + }) : super(key: key); + + @override + _GoogleMapTabState createState() => _GoogleMapTabState(); +} + +class _GoogleMapTabState extends State{ + late GoogleMapController _controller; + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + Set markers = {}; + CameraPosition position = CameraPosition( + target: widget.userPosition, + zoom: 13 + ); + + Marker userPosition = Marker( + markerId: const MarkerId('user_position'), + infoWindow: const InfoWindow( + title: 'Это вы' + ), + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueRed), + position: widget.userPosition + ); + + markers.add(userPosition); + + for(Map departmentInfo in widget.departmentsInfo) { + List coordinates = departmentInfo['coordinates'].map((String element) => double.parse(element.trim())).toList(); + LatLng departmentPosition = LatLng(coordinates[0], coordinates[1]); + + markers.add(Marker( + markerId: const MarkerId('user_position'), + infoWindow: InfoWindow( + title: departmentInfo['name'].toString() + " ${departmentInfo['sign']}${departmentInfo['currency_rate']}", + snippet: " (в ${departmentInfo['distance'].toString()} метрах от вас)" + ), + icon: BitmapDescriptor.defaultMarkerWithHue(BitmapDescriptor.hueGreen), + position: departmentPosition + )); + } + + return Scaffold( + body: Column( + children: [ + Expanded( + child: GoogleMap( + initialCameraPosition: position, + onMapCreated: (controller) { + _controller = controller; + }, + markers: markers, + zoomControlsEnabled: false, + zoomGesturesEnabled: true, + scrollGesturesEnabled: true, + compassEnabled: true, + rotateGesturesEnabled: true, + mapToolbarEnabled: true, + tiltGesturesEnabled: true, + gestureRecognizers: < Factory < OneSequenceGestureRecognizer >> [ + new Factory < OneSequenceGestureRecognizer > ( + () => new EagerGestureRecognizer(), + ), + ].toSet() + ) + ) + ], + ) + ); + } + +} \ No newline at end of file diff --git a/client/lib/ui/home.dart b/client/lib/ui/home.dart index aa21ed1..3ffbf48 100644 --- a/client/lib/ui/home.dart +++ b/client/lib/ui/home.dart @@ -5,6 +5,7 @@ import 'package:client/ui/body/department_list.dart'; import 'package:client/services/data/department.dart'; import 'package:client/ui/body/settings.dart'; import 'package:flutter/material.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:http/http.dart' as http; import 'package:geolocator/geolocator.dart'; import 'package:client/ui/errors/department/department_error.dart'; @@ -13,6 +14,7 @@ import 'package:client/ui/errors/department/department_empty.dart'; import 'package:client/ui/errors/gps/gps_denied.dart'; import 'package:client/ui/errors/gps/gps_disabled.dart'; import 'package:client/ui/errors/gps/gps_waiting.dart'; +import 'package:client/ui/body/map.dart'; import 'dart:convert'; class Home extends StatefulWidget { @@ -23,7 +25,8 @@ class Home extends StatefulWidget { } class _HomeState extends State with TickerProviderStateMixin { - late final TabController _tabController = TabController(length: 2, vsync: this); + late final TabController _tabController = TabController(length: 3, vsync: this); + bool _refreshButtonVisible = true; DepartmentState _requestState = DepartmentState.loading; List _departments = []; @@ -117,14 +120,26 @@ class _HomeState extends State with TickerProviderStateMixin { super.initState(); _tabController.addListener(() { if (!_tabController.indexIsChanging) { - setState(() { - - }); + if ([0, 1].contains(_tabController.index)) { + setState(() { + _refreshButtonVisible = true; + }); + } else { + setState(() { + _refreshButtonVisible = false; + }); + } } }); getNearestDepartments(); } + @override + void dispose() { + _tabController.dispose(); + super.dispose(); + } + void refresh(String currency, String operation, int radius, int departmentNumber) { _currency = currency; _operation = operation; @@ -134,10 +149,12 @@ class _HomeState extends State with TickerProviderStateMixin { _tabController.animateTo(0); _tabController.index = 0; + _refreshButtonVisible = true; + getNearestDepartments(); } - Widget getCurrentWidget() { + Widget getCurrentDepartmentWidget() { if (_gpsState == GpsState.gpsDisabled) { return const GpsDisabled(); } else if (_gpsState == GpsState.waitingForPermission) { @@ -170,6 +187,44 @@ class _HomeState extends State with TickerProviderStateMixin { return const Text(''); } + Widget getCurrentMapWidget() { + if (_gpsState == GpsState.gpsDisabled) { + return const GpsDisabled(); + } else if (_gpsState == GpsState.waitingForPermission) { + return const GpsWaiting(); + } else if (_gpsState == GpsState.permissionDenied) { + return const GpsDenied(); + } else { + if (_requestState == DepartmentState.loading) { + return const DepartmentLoading(); + } else if (_requestState == DepartmentState.success) { + LatLng userPosition = LatLng(_position.latitude, _position.longitude); + // LatLng userPosition = LatLng(53.896171, 27.543516); + + List> departmentsInfo = _departments.map((Department department) { + return { + 'coordinates': department.coordinates, + 'name': department.bankName, + 'distance': department.distance, + 'currency_rate': department.currencyRates[_currency.toLowerCase()]![_operation == 'Buy' ? 'bank_sells' : 'bank_buys' ]!, + 'sign': _currency == 'USD' ? '\$' : '€' + }; + }).toList(); + + return GoogleMapTab( + userPosition: userPosition, + departmentsInfo: departmentsInfo + ); + } else if (_requestState == DepartmentState.error) { + return const DepartmentError(); + } else if (_requestState == DepartmentState.empty) { + return const DepartmentEmpty(); + } + } + + return Text(''); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -178,16 +233,20 @@ class _HomeState extends State with TickerProviderStateMixin { controller: _tabController, physics: const BouncingScrollPhysics(), children: [ - getCurrentWidget(), - Settings(refresh: refresh) + getCurrentDepartmentWidget(), + Settings(refresh: refresh), + getCurrentMapWidget() ], ), - floatingActionButton: FloatingActionButton( - onPressed: () { - refresh(_currency, _operation, _radius, _departmentNumber); - }, - child: const Icon(Icons.refresh) - ), + floatingActionButton: Visibility( + visible: _refreshButtonVisible, + child: FloatingActionButton( + onPressed: () { + refresh(_currency, _operation, _radius, _departmentNumber); + }, + child: const Icon(Icons.refresh) + ), + ) ); } } \ No newline at end of file diff --git a/client/pubspec.lock b/client/pubspec.lock index 7990c6c..a4e587e 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -97,6 +97,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" flutter_test: dependency: "direct dev" description: flutter @@ -149,6 +156,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.1.0" + google_maps_flutter: + dependency: "direct main" + description: + name: google_maps_flutter + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.3" + google_maps_flutter_platform_interface: + dependency: transitive + description: + name: google_maps_flutter_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.5" http: dependency: "direct main" description: @@ -252,6 +273,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" + stream_transform: + dependency: transitive + description: + name: stream_transform + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" string_scanner: dependency: transitive description: diff --git a/client/pubspec.yaml b/client/pubspec.yaml index 35cc19a..79bfb3f 100644 --- a/client/pubspec.yaml +++ b/client/pubspec.yaml @@ -31,7 +31,7 @@ dependencies: sdk: flutter http: 0.13.4 geolocator: ^8.2.0 - + google_maps_flutter: ^2.1.3 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons.