Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:flutter_map_example/pages/debouncing_tile_update_transformer.dar
import 'package:flutter_map_example/pages/epsg3996_crs.dart';
import 'package:flutter_map_example/pages/epsg4326_crs.dart';
import 'package:flutter_map_example/pages/fallback_url_page.dart';
import 'package:flutter_map_example/pages/fling_animation_damping.dart';
import 'package:flutter_map_example/pages/home.dart';
import 'package:flutter_map_example/pages/interactive_test_page.dart';
import 'package:flutter_map_example/pages/latlng_to_screen_point.dart';
Expand Down Expand Up @@ -77,6 +78,8 @@ class MyApp extends StatelessWidget {
const TileLoadingErrorHandle(),
TileBuilderPage.route: (context) => const TileBuilderPage(),
InteractiveFlagsPage.route: (context) => const InteractiveFlagsPage(),
FlingAnimationDampingPage.route: (context) =>
const FlingAnimationDampingPage(),
ManyMarkersPage.route: (context) => const ManyMarkersPage(),
MapInsideListViewPage.route: (context) => const MapInsideListViewPage(),
ResetTileLayerPage.route: (context) => const ResetTileLayerPage(),
Expand Down
129 changes: 129 additions & 0 deletions example/lib/pages/fling_animation_damping.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_example/misc/tile_providers.dart';
import 'package:flutter_map_example/widgets/drawer/menu_drawer.dart';
import 'package:latlong2/latlong.dart';

class FlingAnimationDampingPage extends StatefulWidget {
static const String route = '/fling_animation_damping';

const FlingAnimationDampingPage({super.key});

@override
State<FlingAnimationDampingPage> createState() =>
_FlingAnimationDampingPageState();
}

class _FlingAnimationDampingPageState extends State<FlingAnimationDampingPage> {
double _dampingRatio = 2;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Fling Animation Damping')),
drawer: const MenuDrawer(FlingAnimationDampingPage.route),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Damping Ratio: ${_dampingRatio.toStringAsFixed(1)}',
style: Theme.of(context).textTheme.titleMedium,
textAlign: TextAlign.center,
),
const SizedBox(height: 8),
const Text(
'Drag the map and release to see the fling animation. '
'Lower values = less momentum, stops quicker. '
'Higher values = more momentum, bouncier feel. ',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 12),
),
const SizedBox(height: 8),
Row(
children: [
const Text('Damped (1)'),
Expanded(
child: Slider(
value: _dampingRatio,
min: 1,
max: 10,
divisions: 19,
label: _dampingRatio.toStringAsFixed(1),
onChanged: (value) {
setState(() {
_dampingRatio = value;
});
},
),
),
const Text('Damped (10)'),
],
),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
alignment: WrapAlignment.center,
children: [
ElevatedButton(
onPressed: () => setState(() => _dampingRatio = 1),
child: const Text('Very Damped (1)'),
),
ElevatedButton(
onPressed: () => setState(() => _dampingRatio = 2),
child: const Text('Damped (2)'),
),
ElevatedButton(
onPressed: () => setState(() => _dampingRatio = 5),
child: const Text('Default (5)'),
),
ElevatedButton(
onPressed: () => setState(() => _dampingRatio = 7),
child: const Text('Bouncy (4)'),
),
ElevatedButton(
onPressed: () => setState(() => _dampingRatio = 10),
child: const Text('Very Bouncy (10)'),
),
],
),
],
),
),
Expanded(
child: FlutterMap(
options: MapOptions(
initialCenter: const LatLng(51.5, -0.09),
initialZoom: 11,
interactionOptions: InteractionOptions(
flags: InteractiveFlag.all,
flingAnimationDampingRatio: _dampingRatio,
),
),
children: [
openStreetMapTileLayer,
Center(
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.8),
borderRadius: BorderRadius.circular(4),
),
child: const Text(
'Drag and release to see the fling effect!',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
),
],
),
),
],
),
);
}
}
6 changes: 6 additions & 0 deletions example/lib/widgets/drawer/menu_drawer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:flutter_map_example/pages/debouncing_tile_update_transformer.dar
import 'package:flutter_map_example/pages/epsg3996_crs.dart';
import 'package:flutter_map_example/pages/epsg4326_crs.dart';
import 'package:flutter_map_example/pages/fallback_url_page.dart';
import 'package:flutter_map_example/pages/fling_animation_damping.dart';
import 'package:flutter_map_example/pages/home.dart';
import 'package:flutter_map_example/pages/interactive_test_page.dart';
import 'package:flutter_map_example/pages/latlng_to_screen_point.dart';
Expand Down Expand Up @@ -189,6 +190,11 @@ class MenuDrawer extends StatelessWidget {
routeName: InteractiveFlagsPage.route,
currentRoute: currentRoute,
),
MenuItemWidget(
caption: 'Fling Animation Damping',
routeName: FlingAnimationDampingPage.route,
currentRoute: currentRoute,
),
const Divider(),
MenuItemWidget(
caption: 'WMS Sourced Map',
Expand Down
13 changes: 9 additions & 4 deletions lib/src/gestures/map_interactive_viewer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -789,13 +789,18 @@ class MapInteractiveViewerState extends State<MapInteractiveViewer>
return;
}

final direction = details.velocity.pixelsPerSecond / magnitude;
// Use the actual tracked offset to determine direction instead of the
// velocity direction, which can be incorrect on web when the pointer
// leaves the window.
final flingOffset = _focalStartLocal - _lastFocalLocal;
final flingDistance = flingOffset.distance;

final direction = flingOffset / flingDistance;
final distance = (Offset.zero & _camera.nonRotatedSize).shortestSide;

final flingOffset = _focalStartLocal - _lastFocalLocal;
_flingAnimation = Tween<Offset>(
begin: flingOffset,
end: flingOffset - direction * distance,
end: flingOffset + direction * distance,
).animate(_flingController);

_flingController
Expand All @@ -805,7 +810,7 @@ class MapInteractiveViewerState extends State<MapInteractiveViewer>
springDescription: SpringDescription.withDampingRatio(
mass: 1,
stiffness: 1000,
ratio: 5,
ratio: _interactionOptions.flingAnimationDampingRatio,
));
}

Expand Down
16 changes: 16 additions & 0 deletions lib/src/map/options/interaction.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ class InteractionOptions {
/// Defaults to [Curves.fastOutSlowIn].
final Curve doubleTapZoomCurve;

/// The damping ratio for the fling animation spring simulation
///
/// This controls how the fling animation decelerates after a drag gesture.
/// Lower values result in less damping (more momentum, bouncier).
/// Higher values result in more damping (stops quicker, less bouncy).
///
/// Defaults to 2.0.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't match the default value in the constructor (5.0).

Suggested change
/// Defaults to 2.0.
/// Defaults to 5.0.

final double flingAnimationDampingRatio;

/// Options to configure cursor/keyboard rotation
///
/// Cursor/keyboard rotation is designed for desktop platforms, and allows the
Expand Down Expand Up @@ -129,6 +138,7 @@ class InteractionOptions {
defaultDoubleTapDragZoomChangeCalculator,
this.doubleTapZoomDuration = const Duration(milliseconds: 200),
this.doubleTapZoomCurve = Curves.fastOutSlowIn,
this.flingAnimationDampingRatio = 5.0,
this.cursorKeyboardRotationOptions = const CursorKeyboardRotationOptions(),
this.keyboardOptions = const KeyboardOptions(),
}) : assert(
Expand All @@ -142,6 +152,10 @@ class InteractionOptions {
assert(
pinchMoveThreshold >= 0.0,
'`pinchMoveThreshold` must be positive',
),
assert(
flingAnimationDampingRatio > 0.0,
'`flingAnimationDampingRatio` must be positive',
);

/// Default calculator function for [doubleTapDragZoomChangeCalculator]
Expand Down Expand Up @@ -171,6 +185,7 @@ class InteractionOptions {
other.doubleTapDragZoomChangeCalculator &&
doubleTapZoomDuration == other.doubleTapZoomDuration &&
doubleTapZoomCurve == other.doubleTapZoomCurve &&
flingAnimationDampingRatio == other.flingAnimationDampingRatio &&
keyboardOptions == other.keyboardOptions;

@override
Expand All @@ -188,6 +203,7 @@ class InteractionOptions {
doubleTapDragZoomChangeCalculator,
doubleTapZoomDuration,
doubleTapZoomCurve,
flingAnimationDampingRatio,
keyboardOptions,
);
}