Skip to content

Commit

Permalink
use the animation of the controller in the example app
Browse files Browse the repository at this point in the history
  • Loading branch information
josxha committed Dec 3, 2023
1 parent 96f32f6 commit e5ac81f
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 114 deletions.
108 changes: 6 additions & 102 deletions example/lib/pages/animated_map_controller.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_cancellable_tile_provider/flutter_map_cancellable_tile_provider.dart';
import 'package:flutter_map_example/widgets/drawer/menu_drawer.dart';
import 'package:latlong2/latlong.dart';

Expand All @@ -16,10 +15,6 @@ class AnimatedMapControllerPage extends StatefulWidget {

class AnimatedMapControllerPageState extends State<AnimatedMapControllerPage>
with TickerProviderStateMixin {
static const _startedId = 'AnimatedMapController#MoveStarted';
static const _inProgressId = 'AnimatedMapController#MoveInProgress';
static const _finishedId = 'AnimatedMapController#MoveFinished';

static const _london = LatLng(51.5, -0.09);
static const _paris = LatLng(48.8566, 2.3522);
static const _dublin = LatLng(53.3498, -6.2603);
Expand All @@ -45,61 +40,7 @@ class AnimatedMapControllerPageState extends State<AnimatedMapControllerPage>
),
];

final mapController = MapController();

void _animatedMapMove(LatLng destLocation, double destZoom) {
// Create some tweens. These serve to split up the transition from one location to another.
// In our case, we want to split the transition be<tween> our current map center and the destination.
final camera = mapController.camera;
final latTween = Tween<double>(
begin: camera.center.latitude, end: destLocation.latitude);
final lngTween = Tween<double>(
begin: camera.center.longitude, end: destLocation.longitude);
final zoomTween = Tween<double>(begin: camera.zoom, end: destZoom);

// Create a animation controller that has a duration and a TickerProvider.
final controller = AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
// The animation determines what path the animation will take. You can try different Curves values, although I found
// fastOutSlowIn to be my favorite.
final Animation<double> animation =
CurvedAnimation(parent: controller, curve: Curves.fastOutSlowIn);

// Note this method of encoding the target destination is a workaround.
// When proper animated movement is supported (see #1263) we should be able
// to detect an appropriate animated movement event which contains the
// target zoom/center.
final startIdWithTarget =
'$_startedId#${destLocation.latitude},${destLocation.longitude},$destZoom';
bool hasTriggeredMove = false;

controller.addListener(() {
final String id;
if (animation.value == 1.0) {
id = _finishedId;
} else if (!hasTriggeredMove) {
id = startIdWithTarget;
} else {
id = _inProgressId;
}

hasTriggeredMove |= mapController.move(
LatLng(latTween.evaluate(animation), lngTween.evaluate(animation)),
zoomTween.evaluate(animation),
id: id,
);
});

animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.dispose();
} else if (status == AnimationStatus.dismissed) {
controller.dispose();
}
});

controller.forward();
}
final mapController = MapController() as MapControllerImpl;

@override
Widget build(BuildContext context) {
Expand All @@ -115,15 +56,15 @@ class AnimatedMapControllerPageState extends State<AnimatedMapControllerPage>
child: Row(
children: <Widget>[
MaterialButton(
onPressed: () => _animatedMapMove(_london, 10),
onPressed: () => mapController.moveAnimated(_london, 10),
child: const Text('London'),
),
MaterialButton(
onPressed: () => _animatedMapMove(_paris, 5),
onPressed: () => mapController.moveAnimated(_paris, 5),
child: const Text('Paris'),
),
MaterialButton(
onPressed: () => _animatedMapMove(_dublin, 5),
onPressed: () => mapController.moveAnimated(_dublin, 5),
child: const Text('Dublin'),
),
],
Expand Down Expand Up @@ -161,7 +102,8 @@ class AnimatedMapControllerPageState extends State<AnimatedMapControllerPage>
final constrained = CameraFit.bounds(
bounds: bounds,
).fit(mapController.camera);
_animatedMapMove(constrained.center, constrained.zoom);
mapController.moveAnimated(
constrained.center, constrained.zoom);
},
child: const Text('Fit Bounds animated'),
),
Expand All @@ -182,8 +124,6 @@ class AnimatedMapControllerPageState extends State<AnimatedMapControllerPage>
urlTemplate:
'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
userAgentPackageName: 'dev.fleaflet.flutter_map.example',
tileProvider: CancellableNetworkTileProvider(),
tileUpdateTransformer: _animatedMoveTileUpdateTransformer,
),
const MarkerLayer(markers: _markers),
],
Expand All @@ -195,39 +135,3 @@ class AnimatedMapControllerPageState extends State<AnimatedMapControllerPage>
);
}
}

/// Causes tiles to be prefetched at the target location and disables pruning
/// whilst animating movement. When proper animated movement is added (see
/// #1263) we should just detect the appropriate AnimatedMove events and
/// use their target zoom/center.
final _animatedMoveTileUpdateTransformer =
TileUpdateTransformer.fromHandlers(handleData: (updateEvent, sink) {
final mapEvent = updateEvent.mapEvent;

final id = mapEvent is MapEventMove ? mapEvent.id : null;
if (id?.startsWith(AnimatedMapControllerPageState._startedId) ?? false) {
final parts = id!.split('#')[2].split(',');
final lat = double.parse(parts[0]);
final lon = double.parse(parts[1]);
final zoom = double.parse(parts[2]);

// When animated movement starts load tiles at the target location and do
// not prune. Disabling pruning means existing tiles will remain visible
// whilst animating.
sink.add(
updateEvent.loadOnly(
loadCenterOverride: LatLng(lat, lon),
loadZoomOverride: zoom,
),
);
} else if (id == AnimatedMapControllerPageState._inProgressId) {
// Do not prune or load whilst animating so that any existing tiles remain
// visible. A smarter implementation may start pruning once we are close to
// the target zoom/location.
} else if (id == AnimatedMapControllerPageState._finishedId) {
// We already prefetched the tiles when animation started so just prune.
sink.add(updateEvent.pruneOnly());
} else {
sink.add(updateEvent);
}
});
28 changes: 24 additions & 4 deletions example/lib/pages/plugin_zoombuttons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,39 @@ import 'package:flutter_map_example/plugins/zoombuttons_plugin.dart';
import 'package:flutter_map_example/widgets/drawer/menu_drawer.dart';
import 'package:latlong2/latlong.dart';

class PluginZoomButtons extends StatelessWidget {
class PluginZoomButtons extends StatefulWidget {
static const String route = '/plugin_zoombuttons';

const PluginZoomButtons({super.key});

@override
State<PluginZoomButtons> createState() => _PluginZoomButtonsState();
}

class _PluginZoomButtonsState extends State<PluginZoomButtons> {
double? zoom;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Zoom Buttons Plugin')),
appBar: AppBar(
title: const Text('Zoom Buttons Plugin'),
actions: [
Padding(
padding: const EdgeInsets.only(right: 50),
child: Text(zoom?.toString() ?? '?'),
),
],
),
drawer: const MenuDrawer(PluginZoomButtons.route),
body: FlutterMap(
options: const MapOptions(
initialCenter: LatLng(51.5, -0.09),
options: MapOptions(
onPositionChanged: (position, hasGesture) {
setState(() {
zoom = position.zoom;
});
},
initialCenter: const LatLng(51.5, -0.09),
initialZoom: 5,
),
children: [
Expand Down
12 changes: 4 additions & 8 deletions example/lib/plugins/zoombuttons_plugin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class FlutterMapZoomButtons extends StatelessWidget {

@override
Widget build(BuildContext context) {
final controller = MapController.of(context);
final controller = MapController.of(context) as MapControllerImpl;
final camera = MapCamera.of(context);
final theme = Theme.of(context);

Expand All @@ -52,12 +52,8 @@ class FlutterMapZoomButtons extends StatelessWidget {
mini: mini,
backgroundColor: zoomInColor ?? theme.primaryColor,
onPressed: () {
final paddedMapCamera = CameraFit.bounds(
bounds: camera.visibleBounds,
padding: _fitBoundsPadding,
).fit(camera);
final zoom = min(paddedMapCamera.zoom + 1, maxZoom);
controller.move(paddedMapCamera.center, zoom);
final zoom = min(camera.zoom + 1, maxZoom);
controller.moveAnimated(camera.center, zoom);
},
child: Icon(zoomInIcon,
color: zoomInColorIcon ?? theme.iconTheme.color),
Expand All @@ -78,7 +74,7 @@ class FlutterMapZoomButtons extends StatelessWidget {
if (zoom < minZoom) {
zoom = minZoom;
}
controller.move(paddedMapCamera.center, zoom);
controller.moveAnimated(paddedMapCamera.center, zoom);
},
child: Icon(zoomOutIcon,
color: zoomOutColorIcon ?? theme.iconTheme.color),
Expand Down

0 comments on commit e5ac81f

Please sign in to comment.