Hibok
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

365 lignes
11 KiB

  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:ui';
  4. import 'package:android_intent/android_intent.dart';
  5. import 'package:chat/utils/loading_builder.dart';
  6. import 'package:chat/utils/log.dart';
  7. import 'package:chat/utils/screen_shot.dart';
  8. import 'package:flutter/material.dart';
  9. import 'package:flutter/rendering.dart';
  10. import 'package:flutter/services.dart';
  11. import 'package:geolocator/geolocator.dart';
  12. import 'package:google_maps_flutter/google_maps_flutter.dart';
  13. import 'package:provider/provider.dart';
  14. import 'location_provider.dart';
  15. import 'location_result.dart';
  16. import 'package:http/http.dart' as http;
  17. class MapPicker extends StatefulWidget {
  18. final LatLng initialCenter;
  19. final String apiKey;
  20. final bool requiredGPS;
  21. const MapPicker({
  22. Key key,
  23. this.initialCenter,
  24. this.apiKey,
  25. this.requiredGPS,
  26. }) : super(key: key);
  27. @override
  28. MapPickerState createState() => MapPickerState();
  29. }
  30. class MapPickerState extends State<MapPicker> {
  31. Completer<GoogleMapController> mapController = Completer();
  32. GlobalKey rootWidgetKey = GlobalKey();
  33. LatLng _lastMapPosition;
  34. Position _currentPosition;
  35. String _address;
  36. // void _onToggleMapTypePressed() {
  37. // final MapType nextType =
  38. // MapType.values[(_currentMapType.index + 1) % MapType.values.length];
  39. // setState(() => _currentMapType = nextType);
  40. // }
  41. // this also checks for location permission.
  42. Future<void> _initCurrentLocation() async {
  43. Position currentPosition;
  44. try {
  45. currentPosition = await Geolocator()
  46. .getCurrentPosition(desiredAccuracy: LocationAccuracy.best);
  47. d("position = $currentPosition");
  48. setState(() => _currentPosition = currentPosition);
  49. } on PlatformException catch (e) {
  50. currentPosition = null;
  51. d("_initCurrentLocation#e = $e");
  52. }
  53. if (!mounted) return;
  54. setState(() => _currentPosition = currentPosition);
  55. if (currentPosition != null)
  56. moveToCurrentLocation(
  57. LatLng(currentPosition.latitude, currentPosition.longitude));
  58. }
  59. @override
  60. void initState() {
  61. super.initState();
  62. _initCurrentLocation();
  63. }
  64. @override
  65. void dispose() {
  66. rootWidgetKey = null;
  67. super.dispose();
  68. }
  69. @override
  70. Widget build(BuildContext context) {
  71. if (widget.requiredGPS) {
  72. _checkGps();
  73. _checkGeolocationPermission();
  74. }
  75. return Scaffold(
  76. body: Builder(builder: (context) {
  77. if (_currentPosition == null && widget.requiredGPS)
  78. return const Center(child: CircularProgressIndicator());
  79. return buildMap();
  80. }),
  81. );
  82. }
  83. Widget buildMap() {
  84. return Center(
  85. child: Stack(
  86. children: <Widget>[
  87. GoogleMap(
  88. onMapCreated: (GoogleMapController controller) {
  89. mapController.complete(controller);
  90. _lastMapPosition = widget.initialCenter;
  91. LocationProvider.of(context)
  92. .setLastIdleLocation(_lastMapPosition);
  93. },
  94. initialCameraPosition: CameraPosition(
  95. target: widget.initialCenter,
  96. zoom: 18.0,
  97. ),
  98. onCameraMove: (CameraPosition position) {
  99. _lastMapPosition = position.target;
  100. },
  101. onCameraIdle: () async {
  102. LocationProvider.of(context)
  103. .setLastIdleLocation(_lastMapPosition);
  104. },
  105. myLocationEnabled: true,
  106. myLocationButtonEnabled: true),
  107. // _MapFabs(
  108. // onToggleMapTypePressed: _onToggleMapTypePressed,
  109. // ),
  110. pin(),
  111. locationCard(),
  112. ],
  113. ),
  114. );
  115. }
  116. Widget locationCard() {
  117. return Align(
  118. alignment: Alignment.bottomCenter,
  119. child: Padding(
  120. padding: const EdgeInsets.fromLTRB(8, 8, 8, 24),
  121. child: Card(
  122. child: Padding(
  123. padding: const EdgeInsets.all(8),
  124. child: Consumer<LocationProvider>(
  125. builder: (context, locationProvider, _) {
  126. return Row(
  127. mainAxisAlignment: MainAxisAlignment.spaceBetween,
  128. children: <Widget>[
  129. Flexible(
  130. flex: 20,
  131. child: FutureLoadingBuilder<String>(
  132. future: getAddress(locationProvider.lastIdleLocation),
  133. mutable: true,
  134. loadingIndicator: Row(
  135. mainAxisAlignment: MainAxisAlignment.center,
  136. children: <Widget>[
  137. CircularProgressIndicator(),
  138. ],
  139. ),
  140. builder: (context, address) {
  141. _address = address;
  142. return Column(
  143. mainAxisSize: MainAxisSize.min,
  144. children: [
  145. Text(
  146. address ?? 'Unnamed place',
  147. textScaleFactor: 1.0,
  148. style: TextStyle(
  149. fontSize: 18,
  150. ),
  151. ),
  152. ],
  153. );
  154. }),
  155. ),
  156. Spacer(),
  157. FloatingActionButton(
  158. onPressed: () async {
  159. var capture = await ScreenShot.takeScreenshotImage();
  160. Navigator.of(context).pop({
  161. 'location': LocationResult(
  162. latLng: locationProvider.lastIdleLocation,
  163. address: _address,
  164. screen: capture),
  165. });
  166. },
  167. child: Icon(Icons.send, color: Colors.white),
  168. ),
  169. ],
  170. );
  171. }),
  172. ),
  173. ),
  174. ),
  175. );
  176. }
  177. Future<String> getAddress(LatLng latLng) async {
  178. try {
  179. /*
  180. var placeMarks = await Geolocator()
  181. .placemarkFromCoordinates(latLng.latitude, latLng.longitude);
  182. if (placeMarks == null) {
  183. return null;
  184. }
  185. var place = placeMarks.first;
  186. print('place ${place.toJson()}');
  187. return place.name;
  188. */
  189. var endPoint =
  190. 'https://maps.googleapis.com/maps/api/geocode/json?latlng=${latLng?.latitude},${latLng?.longitude}&key=${widget.apiKey}';
  191. var response = jsonDecode((await http.get(endPoint)).body);
  192. return response['results'][0]['formatted_address'];
  193. } catch (e) {
  194. print(e);
  195. }
  196. return null;
  197. }
  198. Center pin() {
  199. return Center(
  200. child: Column(
  201. mainAxisAlignment: MainAxisAlignment.center,
  202. children: <Widget>[
  203. Icon(Icons.place, size: 56, color: Colors.lightBlue),
  204. Container(
  205. decoration: ShapeDecoration(
  206. shadows: [
  207. BoxShadow(
  208. color: Colors.black38,
  209. blurRadius: 4,
  210. ),
  211. ],
  212. shape: CircleBorder(
  213. side: BorderSide(
  214. width: 4,
  215. color: Colors.transparent,
  216. ),
  217. ),
  218. ),
  219. ),
  220. SizedBox(height: 56),
  221. ],
  222. ),
  223. );
  224. }
  225. Future moveToCurrentLocation(LatLng currentLocation) async {
  226. var controller = await mapController.future;
  227. controller.animateCamera(CameraUpdate.newCameraPosition(
  228. CameraPosition(target: currentLocation, zoom: 18.0),
  229. ));
  230. }
  231. var dialogOpen;
  232. Future _checkGeolocationPermission() async {
  233. var geolocationStatus =
  234. await Geolocator().checkGeolocationPermissionStatus();
  235. if (geolocationStatus == GeolocationStatus.denied && dialogOpen == null) {
  236. d('showDialog');
  237. dialogOpen = showDialog(
  238. context: context,
  239. barrierDismissible: false,
  240. builder: (context) {
  241. return AlertDialog(
  242. title: Text('Access to location denied'),
  243. content: Text('Allow access to the location services.'),
  244. actions: <Widget>[
  245. FlatButton(
  246. child: Text('Ok'),
  247. onPressed: () {
  248. Navigator.of(context, rootNavigator: true).pop();
  249. _initCurrentLocation();
  250. dialogOpen = null;
  251. },
  252. ),
  253. ],
  254. );
  255. },
  256. );
  257. //} else if (geolocationStatus == GeolocationStatus.disabled) {
  258. } else if (geolocationStatus == GeolocationStatus.granted) {
  259. d('GeolocationStatus.granted');
  260. if (dialogOpen != null) {
  261. Navigator.of(context, rootNavigator: true).pop();
  262. dialogOpen = null;
  263. }
  264. }
  265. }
  266. Future _checkGps() async {
  267. if (!(await Geolocator().isLocationServiceEnabled())) {
  268. if (Theme.of(context).platform == TargetPlatform.android) {
  269. showDialog(
  270. context: context,
  271. barrierDismissible: false,
  272. builder: (BuildContext context) {
  273. return AlertDialog(
  274. title: Text("Can't get current location"),
  275. content: Text('Please make sure you enable GPS and try again'),
  276. actions: <Widget>[
  277. FlatButton(
  278. child: Text('Ok'),
  279. onPressed: () {
  280. final AndroidIntent intent = AndroidIntent(
  281. action: 'android.settings.LOCATION_SOURCE_SETTINGS');
  282. intent.launch();
  283. Navigator.of(context, rootNavigator: true).pop();
  284. },
  285. ),
  286. ],
  287. );
  288. },
  289. );
  290. }
  291. }
  292. }
  293. }
  294. /*
  295. class _MapFabs extends StatelessWidget {
  296. const _MapFabs({
  297. Key key,
  298. @required this.onToggleMapTypePressed,
  299. }) : assert(onToggleMapTypePressed != null),
  300. super(key: key);
  301. final VoidCallback onToggleMapTypePressed;
  302. @override
  303. Widget build(BuildContext context) {
  304. return Container(
  305. alignment: Alignment.topRight,
  306. margin: const EdgeInsets.only(top: 64, right: 8),
  307. child: Column(
  308. children: <Widget>[
  309. FloatingActionButton(
  310. onPressed: onToggleMapTypePressed,
  311. materialTapTargetSize: MaterialTapTargetSize.padded,
  312. mini: true,
  313. child: const Icon(Icons.layers, size: 28, color: Colors.white),
  314. heroTag: "layers",
  315. ),
  316. ],
  317. ),
  318. );
  319. }
  320. }*/