Skip to content

Commit 8be2db8

Browse files
authored
feat: allow fling animation damping to be customised (#2177)
1 parent decf89b commit 8be2db8

File tree

5 files changed

+155
-1
lines changed

5 files changed

+155
-1
lines changed

example/lib/main.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:flutter_map_example/pages/debouncing_tile_update_transformer.dar
77
import 'package:flutter_map_example/pages/epsg3996_crs.dart';
88
import 'package:flutter_map_example/pages/epsg4326_crs.dart';
99
import 'package:flutter_map_example/pages/fallback_url_page.dart';
10+
import 'package:flutter_map_example/pages/fling_animation_damping.dart';
1011
import 'package:flutter_map_example/pages/home.dart';
1112
import 'package:flutter_map_example/pages/interactive_test_page.dart';
1213
import 'package:flutter_map_example/pages/latlng_to_screen_point.dart';
@@ -79,6 +80,8 @@ class MyApp extends StatelessWidget {
7980
TileBuilderPage.route: (context) => const TileBuilderPage(),
8081
ErrorTileBuilder.route: (context) => const ErrorTileBuilder(),
8182
InteractiveFlagsPage.route: (context) => const InteractiveFlagsPage(),
83+
FlingAnimationDampingPage.route: (context) =>
84+
const FlingAnimationDampingPage(),
8285
ManyMarkersPage.route: (context) => const ManyMarkersPage(),
8386
MapInsideListViewPage.route: (context) => const MapInsideListViewPage(),
8487
ResetTileLayerPage.route: (context) => const ResetTileLayerPage(),
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_map/flutter_map.dart';
3+
import 'package:flutter_map_example/misc/tile_providers.dart';
4+
import 'package:flutter_map_example/widgets/drawer/menu_drawer.dart';
5+
import 'package:latlong2/latlong.dart';
6+
7+
class FlingAnimationDampingPage extends StatefulWidget {
8+
static const String route = '/fling_animation_damping';
9+
10+
const FlingAnimationDampingPage({super.key});
11+
12+
@override
13+
State<FlingAnimationDampingPage> createState() =>
14+
_FlingAnimationDampingPageState();
15+
}
16+
17+
class _FlingAnimationDampingPageState extends State<FlingAnimationDampingPage> {
18+
double _dampingRatio = 2;
19+
20+
@override
21+
Widget build(BuildContext context) {
22+
return Scaffold(
23+
appBar: AppBar(title: const Text('Fling Animation Damping')),
24+
drawer: const MenuDrawer(FlingAnimationDampingPage.route),
25+
body: Column(
26+
children: [
27+
Padding(
28+
padding: const EdgeInsets.all(16),
29+
child: Column(
30+
crossAxisAlignment: CrossAxisAlignment.stretch,
31+
children: [
32+
Text(
33+
'Damping Ratio: ${_dampingRatio.toStringAsFixed(1)}',
34+
style: Theme.of(context).textTheme.titleMedium,
35+
textAlign: TextAlign.center,
36+
),
37+
const SizedBox(height: 8),
38+
const Text(
39+
'Drag the map and release to see the fling animation. '
40+
'Lower values = less momentum, stops quicker. '
41+
'Higher values = more momentum, bouncier feel. ',
42+
textAlign: TextAlign.center,
43+
style: TextStyle(fontSize: 12),
44+
),
45+
const SizedBox(height: 8),
46+
Row(
47+
children: [
48+
const Text('Damped (1)'),
49+
Expanded(
50+
child: Slider(
51+
value: _dampingRatio,
52+
min: 1,
53+
max: 10,
54+
divisions: 19,
55+
label: _dampingRatio.toStringAsFixed(1),
56+
onChanged: (value) {
57+
setState(() {
58+
_dampingRatio = value;
59+
});
60+
},
61+
),
62+
),
63+
const Text('Damped (10)'),
64+
],
65+
),
66+
const SizedBox(height: 8),
67+
Wrap(
68+
spacing: 8,
69+
runSpacing: 8,
70+
alignment: WrapAlignment.center,
71+
children: [
72+
ElevatedButton(
73+
onPressed: () => setState(() => _dampingRatio = 1),
74+
child: const Text('Very Damped (1)'),
75+
),
76+
ElevatedButton(
77+
onPressed: () => setState(() => _dampingRatio = 2),
78+
child: const Text('Damped (2)'),
79+
),
80+
ElevatedButton(
81+
onPressed: () => setState(() => _dampingRatio = 5),
82+
child: const Text('Default (5)'),
83+
),
84+
ElevatedButton(
85+
onPressed: () => setState(() => _dampingRatio = 7),
86+
child: const Text('Bouncy (4)'),
87+
),
88+
ElevatedButton(
89+
onPressed: () => setState(() => _dampingRatio = 10),
90+
child: const Text('Very Bouncy (10)'),
91+
),
92+
],
93+
),
94+
],
95+
),
96+
),
97+
Expanded(
98+
child: FlutterMap(
99+
options: MapOptions(
100+
initialCenter: const LatLng(51.5, -0.09),
101+
initialZoom: 11,
102+
interactionOptions: InteractionOptions(
103+
flags: InteractiveFlag.all,
104+
flingAnimationDampingRatio: _dampingRatio,
105+
),
106+
),
107+
children: [
108+
openStreetMapTileLayer,
109+
Center(
110+
child: Container(
111+
padding: const EdgeInsets.all(8),
112+
decoration: BoxDecoration(
113+
color: Colors.white.withValues(alpha: 0.8),
114+
borderRadius: BorderRadius.circular(4),
115+
),
116+
child: const Text(
117+
'Drag and release to see the fling effect!',
118+
style: TextStyle(fontWeight: FontWeight.bold),
119+
),
120+
),
121+
),
122+
],
123+
),
124+
),
125+
],
126+
),
127+
);
128+
}
129+
}

example/lib/widgets/drawer/menu_drawer.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:flutter_map_example/pages/debouncing_tile_update_transformer.dar
1111
import 'package:flutter_map_example/pages/epsg3996_crs.dart';
1212
import 'package:flutter_map_example/pages/epsg4326_crs.dart';
1313
import 'package:flutter_map_example/pages/fallback_url_page.dart';
14+
import 'package:flutter_map_example/pages/fling_animation_damping.dart';
1415
import 'package:flutter_map_example/pages/home.dart';
1516
import 'package:flutter_map_example/pages/interactive_test_page.dart';
1617
import 'package:flutter_map_example/pages/latlng_to_screen_point.dart';
@@ -190,6 +191,11 @@ class MenuDrawer extends StatelessWidget {
190191
routeName: InteractiveFlagsPage.route,
191192
currentRoute: currentRoute,
192193
),
194+
MenuItemWidget(
195+
caption: 'Fling Animation Damping',
196+
routeName: FlingAnimationDampingPage.route,
197+
currentRoute: currentRoute,
198+
),
193199
const Divider(),
194200
MenuItemWidget(
195201
caption: 'WMS Sourced Map',

lib/src/gestures/map_interactive_viewer.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ class MapInteractiveViewerState extends State<MapInteractiveViewer>
809809
springDescription: SpringDescription.withDampingRatio(
810810
mass: 1,
811811
stiffness: 1000,
812-
ratio: 5,
812+
ratio: _interactionOptions.flingAnimationDampingRatio,
813813
));
814814
}
815815

lib/src/map/options/interaction.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ class InteractionOptions {
8888
/// Defaults to [Curves.fastOutSlowIn].
8989
final Curve doubleTapZoomCurve;
9090

91+
/// The damping ratio for the fling animation spring simulation
92+
///
93+
/// This controls how the fling animation decelerates after a drag gesture.
94+
/// Lower values result in less damping (more momentum, bouncier).
95+
/// Higher values result in more damping (stops quicker, less bouncy).
96+
///
97+
/// Defaults to 5.0.
98+
final double flingAnimationDampingRatio;
99+
91100
/// Options to configure cursor/keyboard rotation
92101
///
93102
/// Cursor/keyboard rotation is designed for desktop platforms, and allows the
@@ -129,6 +138,7 @@ class InteractionOptions {
129138
defaultDoubleTapDragZoomChangeCalculator,
130139
this.doubleTapZoomDuration = const Duration(milliseconds: 200),
131140
this.doubleTapZoomCurve = Curves.fastOutSlowIn,
141+
this.flingAnimationDampingRatio = 5.0,
132142
this.cursorKeyboardRotationOptions = const CursorKeyboardRotationOptions(),
133143
this.keyboardOptions = const KeyboardOptions(),
134144
}) : assert(
@@ -142,6 +152,10 @@ class InteractionOptions {
142152
assert(
143153
pinchMoveThreshold >= 0.0,
144154
'`pinchMoveThreshold` must be positive',
155+
),
156+
assert(
157+
flingAnimationDampingRatio > 0.0,
158+
'`flingAnimationDampingRatio` must be positive',
145159
);
146160

147161
/// Default calculator function for [doubleTapDragZoomChangeCalculator]
@@ -171,6 +185,7 @@ class InteractionOptions {
171185
other.doubleTapDragZoomChangeCalculator &&
172186
doubleTapZoomDuration == other.doubleTapZoomDuration &&
173187
doubleTapZoomCurve == other.doubleTapZoomCurve &&
188+
flingAnimationDampingRatio == other.flingAnimationDampingRatio &&
174189
keyboardOptions == other.keyboardOptions;
175190

176191
@override
@@ -188,6 +203,7 @@ class InteractionOptions {
188203
doubleTapDragZoomChangeCalculator,
189204
doubleTapZoomDuration,
190205
doubleTapZoomCurve,
206+
flingAnimationDampingRatio,
191207
keyboardOptions,
192208
);
193209
}

0 commit comments

Comments
 (0)