@@ -9,34 +9,28 @@ import 'package:flutter/material.dart';
99import 'package:flutter_gen/gen_l10n/app_localizations.dart' ;
1010import 'package:maplibre_gl/maplibre_gl.dart' ;
1111
12- typedef OnUpdate = void Function (
13- { required double latitude, required double longitude});
12+ typedef OnUpdate = void Function (Coordinates coordinates);
13+ typedef Coordinates = ({ double latitude, double longitude});
1414
1515class LocationField extends StatefulWidget {
16- final double latitude;
17- final double longitude;
16+ final Coordinates ? coordinates;
1817
1918 final OnUpdate onUpdate;
2019
2120 const LocationField (
22- {super .key,
23- required this .longitude,
24- required this .latitude,
25- required this .onUpdate});
21+ {super .key, required this .coordinates, required this .onUpdate});
2622
2723 @override
2824 State <LocationField > createState () => _LocationFieldState ();
2925}
3026
31- double DEFAULT_ZOOM = 8 ;
32- String CURRENT_FEATURE_ID = 'current' ;
33- String TARGET_FEATURE_ID = 'target' ;
27+ const Coordinates BRAZIL_CENTROID_COORDINATES =
28+ (latitude: - 14.235004 , longitude: - 51.92528 );
29+ const double DEFAULT_ZOOMED_OUT_LEVEL = 1.5 ;
30+ const double DEFAULT_ZOOMED_IN_LEVEL = 8 ;
3431
3532class _LocationFieldState extends State <LocationField > {
36- // The latitude value displayed for the marker at the center of the map
37- late double _lat;
38- // The longitude value displayed for the marker at the center of the map
39- late double _lng;
33+ late Coordinates ? _coordinates;
4034
4135 bool _isEditMode = false ;
4236
@@ -47,8 +41,7 @@ class _LocationFieldState extends State<LocationField> {
4741
4842 @override
4943 void initState () {
50- _lat = widget.latitude;
51- _lng = widget.longitude;
44+ _coordinates = widget.coordinates;
5245 super .initState ();
5346 }
5447
@@ -71,10 +64,12 @@ class _LocationFieldState extends State<LocationField> {
7164 const SizedBox (
7265 height: 20 ,
7366 ),
74- Text (t.sightingLocationLongitude (_lng),
75- style: Theme .of (context).textTheme.bodyLarge),
76- Text (t.sightingLocationLatitude (_lat),
77- style: Theme .of (context).textTheme.bodyLarge),
67+ if (_coordinates != null ) ...[
68+ Text (t.sightingLocationLongitude (_coordinates! .longitude),
69+ style: Theme .of (context).textTheme.bodyLarge),
70+ Text (t.sightingLocationLatitude (_coordinates! .latitude),
71+ style: Theme .of (context).textTheme.bodyLarge),
72+ ],
7873 if (_isEditMode)
7974 Padding (
8075 padding: const EdgeInsets .only (top: 10.0 ),
@@ -85,13 +80,20 @@ class _LocationFieldState extends State<LocationField> {
8580 }
8681
8782 Widget _renderMap () {
83+ final initialCameraPosition = _coordinates == null
84+ ? CameraPosition (
85+ zoom: DEFAULT_ZOOMED_OUT_LEVEL ,
86+ target: LatLng (BRAZIL_CENTROID_COORDINATES .latitude,
87+ BRAZIL_CENTROID_COORDINATES .longitude))
88+ : CameraPosition (
89+ target: LatLng (_coordinates! .latitude, _coordinates! .longitude),
90+ zoom: DEFAULT_ZOOMED_IN_LEVEL );
91+
8892 return Container (
8993 alignment: Alignment .center,
9094 height: 200 ,
9195 child: MaplibreMap (
92- initialCameraPosition: CameraPosition (
93- target: LatLng (widget.latitude, widget.longitude),
94- zoom: DEFAULT_ZOOM ),
96+ initialCameraPosition: initialCameraPosition,
9597 scrollGesturesEnabled: _isEditMode,
9698 dragEnabled: _isEditMode,
9799 zoomGesturesEnabled: _isEditMode,
@@ -106,28 +108,33 @@ class _LocationFieldState extends State<LocationField> {
106108 () => EagerGestureRecognizer ()),
107109 },
108110 onStyleLoadedCallback: () async {
109- _existingMapMarker = await _mapController! .addCircle (
110- createMapCircle (lat: widget.latitude, lng: widget.longitude));
111+ if (_coordinates != null ) {
112+ _existingMapMarker = await _mapController! .addCircle (
113+ createMapCircle (
114+ latitude: _coordinates! .latitude,
115+ longitude: _coordinates! .longitude));
116+ }
111117 },
112118 onMapClick: (_, latLng) async {
113119 if (! _isEditMode) return ;
114- if (_existingMapMarker == null ) return ;
115120
116- var nextMarker = _mapController! .circles.where ((c) {
121+ final nextMarker = _mapController! .circles.where ((c) {
117122 return c != _existingMapMarker;
118123 }).firstOrNull;
119124
120125 // Add the "next" marker and update the visuals of the existing marker
121126 if (nextMarker == null ) {
122127 await _mapController! .addCircle (createMapCircle (
123- lat: latLng.latitude, lng: latLng.longitude));
124-
125- await _mapController! .updateCircle (
126- _existingMapMarker! ,
127- const CircleOptions (
128- circleOpacity: 0.5 ,
129- circleStrokeOpacity: 0.5 ,
130- ));
128+ latitude: latLng.latitude, longitude: latLng.longitude));
129+
130+ if (_existingMapMarker != null ) {
131+ await _mapController! .updateCircle (
132+ _existingMapMarker! ,
133+ const CircleOptions (
134+ circleOpacity: 0.5 ,
135+ circleStrokeOpacity: 0.5 ,
136+ ));
137+ }
131138 } else {
132139 await _mapController!
133140 .updateCircle (nextMarker, CircleOptions (geometry: latLng));
@@ -136,57 +143,71 @@ class _LocationFieldState extends State<LocationField> {
136143 _mapController! .animateCamera (CameraUpdate .newLatLng (latLng));
137144
138145 setState (() {
139- _lng = latLng.longitude;
140- _lat = latLng.latitude;
146+ _coordinates =
147+ (latitude : latLng.latitude, longitude : latLng.longitude) ;
141148 });
142149 }));
143150 }
144151
145152 void _handleSave () async {
146153 // Do nothing if coordinates have not changed at all
147- if (_lat == widget.latitude && _lng == widget.longitude ) {
154+ if (_coordinates == widget.coordinates ) {
148155 _handleCancel ();
149156 return ;
150157 }
151158
152159 setState (() {
153160 _isEditMode = false ;
154- widget.onUpdate (latitude: _lat, longitude: _lng);
161+ if (_coordinates != null ) {
162+ widget.onUpdate (_coordinates! );
163+ }
155164 });
156165
157- _resetMap (LatLng (_lat, _lng) );
166+ _resetMap (coordinates : _coordinates ! , zoomLevel : DEFAULT_ZOOMED_IN_LEVEL );
158167 }
159168
160169 void _handleCancel () async {
161- await _resetMap (LatLng (widget.latitude, widget.longitude));
170+ if (widget.coordinates == null || _coordinates == null ) {
171+ await _resetMap (
172+ coordinates: BRAZIL_CENTROID_COORDINATES ,
173+ zoomLevel: DEFAULT_ZOOMED_OUT_LEVEL );
174+ } else {
175+ await _resetMap (
176+ coordinates: widget.coordinates! , zoomLevel: DEFAULT_ZOOMED_IN_LEVEL );
177+ }
162178
163179 setState (() {
164180 _isEditMode = false ;
165- _lat = widget.latitude;
166- _lng = widget.longitude;
181+ _coordinates = widget.coordinates;
167182 });
168183 }
169184
170- Future <void > _resetMap (LatLng latLng) async {
171- await _mapController! .updateCircle (
172- _existingMapMarker! ,
173- CircleOptions (
174- geometry: latLng, circleOpacity: 1.0 , circleStrokeOpacity: 1.0 ));
185+ Future <void > _resetMap (
186+ {required Coordinates coordinates, required double zoomLevel}) async {
187+ final latLng = LatLng (coordinates.latitude, coordinates.longitude);
188+
189+ if (_existingMapMarker != null ) {
190+ await _mapController! .updateCircle (
191+ _existingMapMarker! ,
192+ CircleOptions (
193+ geometry: latLng, circleOpacity: 1.0 , circleStrokeOpacity: 1.0 ));
194+ }
175195
176196 await _mapController! .removeCircles (_mapController! .circles.where ((c) {
177197 return c != _existingMapMarker;
178198 }));
179199
180200 _mapController!
181- .animateCamera (CameraUpdate .newLatLngZoom (latLng, DEFAULT_ZOOM ));
201+ .animateCamera (CameraUpdate .newLatLngZoom (latLng, zoomLevel ));
182202 }
183203}
184204
185- CircleOptions createMapCircle ({required double lat, required double lng}) {
205+ CircleOptions createMapCircle (
206+ {required double latitude, required double longitude}) {
186207 return CircleOptions (
187208 circleRadius: 8 ,
188209 circleColor: MeliColors .magnolia.toHexStringRGB (),
189210 circleStrokeColor: MeliColors .black.toHexStringRGB (),
190211 circleStrokeWidth: 2.0 ,
191- geometry: LatLng (lat, lng ));
212+ geometry: LatLng (latitude, longitude ));
192213}
0 commit comments