Skip to content

Commit 2c60d63

Browse files
rorystephensonjjoelsonJaffaKetchup
authored
[v6] Major State Refactoring (fleaflet#1551)
* Split FlutterMapState in to a stateful container widget (FlutterMapStateContainer) and an immutable representation of the state of the map (FlutterMapState) * Extract interactions to InteractionDetector * Move gesture initialisation out of builder and stop passing the whole FlutterMapStateContainer to InteractionDetector * Minor tidy-ups * Re-instate linking of MapController to map state * Trigger all FlutterMapState manipulations via FlutterMapStateController * Reduce MapController API size and simplify gesture code - Replaced mapState getter with the various getters which were just proxied to MapState. - Heavy refactoring (hopefully without changing behaviour) of gesture code. In passing I have simplified * Remove unnecessary getters now that InteractiveFlags defines convenience methods for checking single flags * Fix double tap zoom not working when drag was enabled and prevent pinch move when only pinch zoom is enabled * Use new InteractiveFlag convenience methods * Combine getBoundsCenterZoom and centerZoomFitBounds * Replace http stubbing with an in-memory TileProvider in tests This stops the following message from being spammed in tests which was caused by a problem with the http mocking: type 'Null' is not a subtype of type 'Future<HttpClientRequest>' * Separate MapOptions from FlutterMapState In doing so I noticed that the adaptive boundary options could use a refactor and so placed them in a dedicated class which led to a tidy up to the boundary code in FlutterMapState. * Combine adaptive bounds, max bounds and sw/ne pan bounds in to a single MapBounds class * Create FrameConstraint and FrameFit abstraction FrameConstraint unites the various methods of setting a maximum bounds for the map frame. Previously MapOptions had three different concepts for setting a max bounds: adaptive bounds, maxBounds and se/nw pan boundaries. Adaptive bounds and maxBounds are now FrameConstraint.contain whilst sw/ne pan boundaries is replaced by FrameConstraint.containCenter. FrameFit is a replacement for FitBoundsOptions, combinining the options with the bounds. This means bounds/boundsOptions now become initialFrameFit (since bounds/boundsOptions were actually initial bounds and the options for those initial bounds). Additionally this sets up an abstraction for different map fits since coordinate fit will be added next. * Add deprecations on MapControllerImpl * Add fitting by coordinates This commit incorporates @jjoelson's coordinate fit implementation in to the new FrameFit abstraction. Co-authored-by: Jonathan Joelson <[email protected]> * Rename FlutterMapState to FlutterMapFrame, add InteractionOptions collection to tidy up options and change how options are propagated in preparation for changing the inherited widget to an inherited model * Use InhertiedModel instead of InheritedWidget * Remove FitCoordinates' inside parameter because fitting inside a set of coordinates doesn't have an unambiguous meaning * Fix event names appearing minified when running web release build * Set constraints that match the old adaptive constraints These old adaptive constraints did not prevent the map from going outside of the specified bounds, they stopped the center of the map from going outside of those bounds. This commit sets the constraints appropriately. * Make documentation easier to read * Use flags/options from InteractionOptions not the old deprecated values, unless InteractiveOptions is not provided * Fix options propagation * Add tests to make sure the InheritedModel notifies if and only if the relevnat aspect changes * Rename FlutterMapFrame to MapFrame * Avoid an extra Stack * Assign AnimationControllers where they are declared * Rename from frame to camera * Remove flutter_map_ prefixes from files in lib/src/map/ * Move FlutterMapStateContainer in to FlutterMap's file since it is just the widget state * Rename mapCamera variables to camera for consistency with options for MapOptions * Documentation * Use standard deprecations format In passing re-ordered the methods in MapControllerImpl to match MapController. * Re-organized camera related source files Improved some documentation (part 1) Prevent public exposure of `FitCoordinates` and `FitBounds` constructors * Add FitInsideBounds * Move CameraFit attributes from the base class to the subclasses and tidy up documentation None of the fields which were on the CameraFit base class were conceptually essential for any imaginable camera fit. Moving them to the subclasses ensures that any future camera fits will not need to implement those options just because they already exist. It would also allow for individual camera fits to specify different default values if appropriate. * Re-order imports alphabetically * Add lint to enforce consistent import/export ordering * Reinstate maxBounds as a deprecated option * Remove duplicate exports and make imports consistent The plugin API no longer exports classes which flutter_map already exports. Imports within this package now import the actual classes they use rather than the whole flutter_map library. Internal code should not depend on the exported library definition. * Remove deprecated AnchorAlign * Add deprecation for nonrotatedSize * Fix deprecation * Set default CameraFit maxZoom values to null The other parameters for CameraFit all have default values which will not cause changes to the calculated CameraFit (i.e. padding is zero, forceIntegerZoomLevel is false). Setting maxZoom to null makes it consistent with the other parameters in that it will not affect the calculated CameraFit. I chose null over double.infinity as in my opinion the intent is clearer, no maximum. --------- Co-authored-by: Jonathan Joelson <[email protected]> Co-authored-by: JaffaKetchup <[email protected]>
1 parent 8ac3ebd commit 2c60d63

File tree

86 files changed

+4910
-2604
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+4910
-2604
lines changed

analysis_options.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ linter:
1616
avoid_dynamic_calls: true
1717
cancel_subscriptions: true
1818
close_sinks: true
19+
directives_ordering: true
1920
package_api_docs: true
2021
prefer_constructors_over_static_methods: true
2122
prefer_final_in_for_each: true
@@ -25,4 +26,4 @@ linter:
2526
throw_in_finally: true
2627
type_annotate_public_apis: true
2728
unnecessary_statements: true
28-
use_named_constants: true
29+
use_named_constants: true

example/lib/main.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:flutter/material.dart';
2-
32
import 'package:flutter_map_example/pages/animated_map_controller.dart';
43
import 'package:flutter_map_example/pages/circle.dart';
54
import 'package:flutter_map_example/pages/custom_crs/custom_crs.dart';

example/lib/pages/animated_map_controller.dart

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ class AnimatedMapControllerPageState extends State<AnimatedMapControllerPage>
3535
void _animatedMapMove(LatLng destLocation, double destZoom) {
3636
// Create some tweens. These serve to split up the transition from one location to another.
3737
// In our case, we want to split the transition be<tween> our current map center and the destination.
38+
final camera = mapController.camera;
3839
final latTween = Tween<double>(
39-
begin: mapController.center.latitude, end: destLocation.latitude);
40+
begin: camera.center.latitude, end: destLocation.latitude);
4041
final lngTween = Tween<double>(
41-
begin: mapController.center.longitude, end: destLocation.longitude);
42-
final zoomTween = Tween<double>(begin: mapController.zoom, end: destZoom);
42+
begin: camera.center.longitude, end: destLocation.longitude);
43+
final zoomTween = Tween<double>(begin: camera.zoom, end: destZoom);
4344

4445
// Create a animation controller that has a duration and a TickerProvider.
4546
final controller = AnimationController(
@@ -161,10 +162,10 @@ class AnimatedMapControllerPageState extends State<AnimatedMapControllerPage>
161162
london,
162163
]);
163164

164-
mapController.fitBounds(
165-
bounds,
166-
options: const FitBoundsOptions(
167-
padding: EdgeInsets.only(left: 15, right: 15),
165+
mapController.fitCamera(
166+
CameraFit.bounds(
167+
bounds: bounds,
168+
padding: const EdgeInsets.symmetric(horizontal: 15),
168169
),
169170
);
170171
},
@@ -178,9 +179,10 @@ class AnimatedMapControllerPageState extends State<AnimatedMapControllerPage>
178179
london,
179180
]);
180181

181-
final centerZoom =
182-
mapController.centerZoomFitBounds(bounds);
183-
_animatedMapMove(centerZoom.center, centerZoom.zoom);
182+
final constrained = CameraFit.bounds(
183+
bounds: bounds,
184+
).fit(mapController.camera);
185+
_animatedMapMove(constrained.center, constrained.zoom);
184186
},
185187
child: const Text('Fit Bounds animated'),
186188
),
@@ -190,9 +192,9 @@ class AnimatedMapControllerPageState extends State<AnimatedMapControllerPage>
190192
Flexible(
191193
child: FlutterMap(
192194
mapController: mapController,
193-
options: MapOptions(
194-
center: const LatLng(51.5, -0.09),
195-
zoom: 5,
195+
options: const MapOptions(
196+
initialCenter: LatLng(51.5, -0.09),
197+
initialZoom: 5,
196198
maxZoom: 10,
197199
minZoom: 3),
198200
children: [

example/lib/pages/circle.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ class CirclePage extends StatelessWidget {
3333
),
3434
Flexible(
3535
child: FlutterMap(
36-
options: MapOptions(
37-
center: const LatLng(51.5, -0.09),
38-
zoom: 11,
36+
options: const MapOptions(
37+
initialCenter: LatLng(51.5, -0.09),
38+
initialZoom: 11,
3939
),
4040
children: [
4141
TileLayer(

example/lib/pages/custom_crs/custom_crs.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ class _CustomCrsPageState extends State<CustomCrsPage> {
128128
options: MapOptions(
129129
// Set the default CRS
130130
crs: epsg3413CRS,
131-
center: LatLng(point.x, point.y),
132-
zoom: 3,
131+
initialCenter: LatLng(point.x, point.y),
132+
initialZoom: 3,
133133
// Set maxZoom usually scales.length - 1 OR resolutions.length - 1
134134
// but not greater
135135
maxZoom: maxZoom,

example/lib/pages/epsg3413_crs.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ class _EPSG3413PageState extends State<EPSG3413Page> {
130130
child: FlutterMap(
131131
options: MapOptions(
132132
crs: epsg3413CRS,
133-
center: const LatLng(90, 0),
134-
zoom: 3,
133+
initialCenter: const LatLng(90, 0),
134+
initialZoom: 3,
135135
maxZoom: maxZoom,
136136
),
137137
nonRotatedChildren: [

example/lib/pages/epsg4326_crs.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ class EPSG4326Page extends StatelessWidget {
2323
),
2424
Flexible(
2525
child: FlutterMap(
26-
options: MapOptions(
26+
options: const MapOptions(
2727
minZoom: 0,
28-
crs: const Epsg4326(),
29-
center: const LatLng(0, 0),
30-
zoom: 0,
28+
crs: Epsg4326(),
29+
initialCenter: LatLng(0, 0),
30+
initialZoom: 0,
3131
),
3232
children: [
3333
TileLayer(
@@ -37,7 +37,7 @@ class EPSG4326Page extends StatelessWidget {
3737
layers: ['TOPO-OSM-WMS'],
3838
),
3939
userAgentPackageName: 'dev.fleaflet.flutter_map.example',
40-
)
40+
),
4141
],
4242
),
4343
),

example/lib/pages/fallback_url.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ class FallbackUrlPage extends StatelessWidget {
4141
Flexible(
4242
child: FlutterMap(
4343
options: MapOptions(
44-
center: center,
45-
zoom: zoom,
44+
initialCenter: center,
45+
initialZoom: zoom,
4646
maxZoom: maxZoom,
4747
minZoom: minZoom,
4848
),

example/lib/pages/home.dart

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,13 @@ class _HomePageState extends State<HomePage> {
112112
Flexible(
113113
child: FlutterMap(
114114
options: MapOptions(
115-
center: const LatLng(51.5, -0.09),
116-
zoom: 5,
117-
maxBounds: LatLngBounds(
118-
const LatLng(-90, -180),
119-
const LatLng(90, 180),
115+
initialCenter: const LatLng(51.5, -0.09),
116+
initialZoom: 5,
117+
cameraConstraint: CameraConstraint.contain(
118+
bounds: LatLngBounds(
119+
const LatLng(-90, -180),
120+
const LatLng(90, 180),
121+
),
120122
),
121123
),
122124
nonRotatedChildren: [

example/lib/pages/interactive_test_page.dart

Lines changed: 62 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
2828
void onMapEvent(MapEvent mapEvent) {
2929
if (mapEvent is! MapEventMove && mapEvent is! MapEventRotate) {
3030
// do not flood console with move and rotate events
31-
debugPrint(mapEvent.toString());
31+
debugPrint(_eventName(mapEvent));
3232
}
3333

3434
setState(() {
@@ -59,7 +59,7 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
5959
mainAxisAlignment: MainAxisAlignment.spaceBetween,
6060
children: <Widget>[
6161
MaterialButton(
62-
color: InteractiveFlag.hasFlag(flags, InteractiveFlag.drag)
62+
color: InteractiveFlag.hasDrag(flags)
6363
? Colors.greenAccent
6464
: Colors.redAccent,
6565
onPressed: () {
@@ -70,8 +70,7 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
7070
child: const Text('Drag'),
7171
),
7272
MaterialButton(
73-
color: InteractiveFlag.hasFlag(
74-
flags, InteractiveFlag.flingAnimation)
73+
color: InteractiveFlag.hasFlingAnimation(flags)
7574
? Colors.greenAccent
7675
: Colors.redAccent,
7776
onPressed: () {
@@ -82,10 +81,9 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
8281
child: const Text('Fling'),
8382
),
8483
MaterialButton(
85-
color:
86-
InteractiveFlag.hasFlag(flags, InteractiveFlag.pinchMove)
87-
? Colors.greenAccent
88-
: Colors.redAccent,
84+
color: InteractiveFlag.hasPinchMove(flags)
85+
? Colors.greenAccent
86+
: Colors.redAccent,
8987
onPressed: () {
9088
setState(() {
9189
updateFlags(InteractiveFlag.pinchMove);
@@ -99,8 +97,7 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
9997
mainAxisAlignment: MainAxisAlignment.spaceBetween,
10098
children: <Widget>[
10199
MaterialButton(
102-
color: InteractiveFlag.hasFlag(
103-
flags, InteractiveFlag.doubleTapZoom)
100+
color: InteractiveFlag.hasDoubleTapZoom(flags)
104101
? Colors.greenAccent
105102
: Colors.redAccent,
106103
onPressed: () {
@@ -111,7 +108,7 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
111108
child: const Text('Double tap zoom'),
112109
),
113110
MaterialButton(
114-
color: InteractiveFlag.hasFlag(flags, InteractiveFlag.rotate)
111+
color: InteractiveFlag.hasRotate(flags)
115112
? Colors.greenAccent
116113
: Colors.redAccent,
117114
onPressed: () {
@@ -122,10 +119,9 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
122119
child: const Text('Rotate'),
123120
),
124121
MaterialButton(
125-
color:
126-
InteractiveFlag.hasFlag(flags, InteractiveFlag.pinchZoom)
127-
? Colors.greenAccent
128-
: Colors.redAccent,
122+
color: InteractiveFlag.hasPinchZoom(flags)
123+
? Colors.greenAccent
124+
: Colors.redAccent,
129125
onPressed: () {
130126
setState(() {
131127
updateFlags(InteractiveFlag.pinchZoom);
@@ -139,7 +135,7 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
139135
padding: const EdgeInsets.only(top: 8, bottom: 8),
140136
child: Center(
141137
child: Text(
142-
'Current event: ${_latestEvent?.runtimeType ?? "none"}\nSource: ${_latestEvent?.source ?? "none"}',
138+
'Current event: ${_eventName(_latestEvent)}\nSource: ${_latestEvent?.source.name ?? "none"}',
143139
textAlign: TextAlign.center,
144140
),
145141
),
@@ -148,9 +144,11 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
148144
child: FlutterMap(
149145
options: MapOptions(
150146
onMapEvent: onMapEvent,
151-
center: const LatLng(51.5, -0.09),
152-
zoom: 11,
153-
interactiveFlags: flags,
147+
initialCenter: const LatLng(51.5, -0.09),
148+
initialZoom: 11,
149+
interactionOptions: InteractionOptions(
150+
flags: flags,
151+
),
154152
),
155153
children: [
156154
TileLayer(
@@ -166,4 +164,49 @@ class _InteractiveTestPageState extends State<InteractiveTestPage> {
166164
),
167165
);
168166
}
167+
168+
String _eventName(MapEvent? event) {
169+
switch (event) {
170+
case MapEventTap():
171+
return 'MapEventTap';
172+
case MapEventSecondaryTap():
173+
return 'MapEventSecondaryTap';
174+
case MapEventLongPress():
175+
return 'MapEventLongPress';
176+
case MapEventMove():
177+
return 'MapEventMove';
178+
case MapEventMoveStart():
179+
return 'MapEventMoveStart';
180+
case MapEventMoveEnd():
181+
return 'MapEventMoveEnd';
182+
case MapEventFlingAnimation():
183+
return 'MapEventFlingAnimation';
184+
case MapEventFlingAnimationNotStarted():
185+
return 'MapEventFlingAnimationNotStarted';
186+
case MapEventFlingAnimationStart():
187+
return 'MapEventFlingAnimationStart';
188+
case MapEventFlingAnimationEnd():
189+
return 'MapEventFlingAnimationEnd';
190+
case MapEventDoubleTapZoom():
191+
return 'MapEventDoubleTapZoom';
192+
case MapEventScrollWheelZoom():
193+
return 'MapEventScrollWheelZoom';
194+
case MapEventDoubleTapZoomStart():
195+
return 'MapEventDoubleTapZoomStart';
196+
case MapEventDoubleTapZoomEnd():
197+
return 'MapEventDoubleTapZoomEnd';
198+
case MapEventRotate():
199+
return 'MapEventRotate';
200+
case MapEventRotateStart():
201+
return 'MapEventRotateStart';
202+
case MapEventRotateEnd():
203+
return 'MapEventRotateEnd';
204+
case MapEventNonRotatedSizeChange():
205+
return 'MapEventNonRotatedSizeChange';
206+
case null:
207+
return 'null';
208+
default:
209+
return 'Unknown';
210+
}
211+
}
169212
}

example/lib/pages/latlng_to_screen_point.dart

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_map/flutter_map.dart';
3-
import 'package:flutter_map_example/widgets/drawer.dart';
43
import 'package:flutter_map/plugin_api.dart';
4+
import 'package:flutter_map_example/widgets/drawer.dart';
55
import 'package:latlong2/latlong.dart';
66

77
class LatLngScreenPointTestPage extends StatefulWidget {
@@ -36,23 +36,24 @@ class _LatLngScreenPointTestPageState extends State<LatLngScreenPointTestPage> {
3636
@override
3737
Widget build(BuildContext context) {
3838
return Scaffold(
39-
appBar: AppBar(title: const Text('LatLng To Screen Point')),
40-
drawer: buildDrawer(context, LatLngScreenPointTestPage.route),
41-
body: Stack(children: [
39+
appBar: AppBar(title: const Text('LatLng To Screen Point')),
40+
drawer: buildDrawer(context, LatLngScreenPointTestPage.route),
41+
body: Stack(
42+
children: [
4243
Padding(
4344
padding: const EdgeInsets.all(8),
4445
child: FlutterMap(
4546
mapController: _mapController,
4647
options: MapOptions(
4748
onMapEvent: onMapEvent,
4849
onTap: (tapPos, latLng) {
49-
final pt1 = _mapController.latLngToScreenPoint(latLng);
50+
final pt1 = _mapController.camera.latLngToScreenPoint(latLng);
5051
_textPos = CustomPoint(pt1.x, pt1.y);
5152
setState(() {});
5253
},
53-
center: const LatLng(51.5, -0.09),
54-
zoom: 11,
55-
rotation: 0,
54+
initialCenter: const LatLng(51.5, -0.09),
55+
initialZoom: 11,
56+
initialRotation: 0,
5657
),
5758
children: [
5859
TileLayer(
@@ -68,6 +69,8 @@ class _LatLngScreenPointTestPageState extends State<LatLngScreenPointTestPage> {
6869
width: 20,
6970
height: 20,
7071
child: const FlutterLogo())
71-
]));
72+
],
73+
),
74+
);
7275
}
7376
}

example/lib/pages/many_markers.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,12 @@ class _ManyMarkersPageState extends State<ManyMarkersPage> {
7272
Text('$_sliderVal markers'),
7373
Flexible(
7474
child: FlutterMap(
75-
options: MapOptions(
76-
center: const LatLng(50, 20),
77-
zoom: 5,
78-
interactiveFlags: InteractiveFlag.all - InteractiveFlag.rotate,
75+
options: const MapOptions(
76+
initialCenter: LatLng(50, 20),
77+
initialZoom: 5,
78+
interactionOptions: InteractionOptions(
79+
flags: InteractiveFlag.all - InteractiveFlag.rotate,
80+
),
7981
),
8082
children: [
8183
TileLayer(

0 commit comments

Comments
 (0)