Skip to content

Commit

Permalink
refacto: upgrade to flutter_map v5
Browse files Browse the repository at this point in the history
  • Loading branch information
TesteurManiak committed Jun 4, 2023
1 parent a7d2a14 commit fcb93fa
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 49 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.4.0

**Check the [Migration Guide](https://github.com/TesteurManiak/flutter_map_animations#v040) to learn about breaking changes in this version**

* Updated to [flutter_map](https://pub.dev/packages/flutter_map/versions/5.0.0) v5
* Updated Dart SDK constraints to `>=3.0.0 <4.0.0`

## 0.3.0

* Updated to [flutter_map](https://pub.dev/packages/flutter_map/versions/4.0.0) v4
Expand Down
36 changes: 33 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,25 @@ Animation utility for the [flutter_map](https://pub.dev/packages/flutter_map) pa

You can try the example app [here](https://testeurmaniak.github.io/flutter_map_animations/#/).

# Table of Contents

- [Documentation](#documentation)
- [AnimatedMapController](#animatedmapcontroller)
- [Animated Movement](#animated-movement)
- [AnimatedMarkerLayer & AnimatedMarker](#animatedmarkerlayer--animatedmarker)
- [Migration Guide](#migration-guide)
- [v0.4.0](#v040)
- [Contributors](#contributors)

# Documentation

## AnimatedMapController

Just create an `AnimatedMapController` and you're good to go:

```dart
class _MyWidgetState extends State<MyWidget> with TickerProviderStateMixin {
late final _mapController = AnimatedMapController(vsync: this);
late final _animatedMapController = AnimatedMapController(vsync: this);
// ...
}
Expand All @@ -32,7 +44,7 @@ And add it to your `FlutterMap` widget:

```dart
FlutterMap(
mapController: _mapController,
mapController: _animatedMapController.mapController,
// ...
)
```
Expand All @@ -56,7 +68,7 @@ All those methods are accessible from the `AnimatedMapController`:

```dart
FlutterMap(
mapController: _mapController,
mapController: _animatedMapController.mapController,
children: [
TileLayer(
urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
Expand All @@ -74,6 +86,24 @@ FlutterMap(
)
```

# Migration Guide

## v0.4.0

* With flutter_map v5 it's not possible anymore to extend `MapControllerImpl` which was used to use the `AnimatedMapController` directly as a `MapController` in the `FlutterMap` widget. Now an instance of `MapController` is created internally or can be passed as a parameter to the `AnimatedMapController` constructor. You can access it with the `mapController` getter:

```dart
late final _animatedMapController = AnimatedMapController(vsync: this);
@override
Widget build(BuildContext context) {
return FlutterMap(
mapController: _animatedMapController.mapController,
// ...
);
}
```

# Contributors

<!-- readme: contributors -start -->
Expand Down
50 changes: 26 additions & 24 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,30 +27,32 @@ class MyHomePage extends StatefulWidget {

class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
static const _useTransformerId = 'useTransformerId';
final _markerSize = 50.0;
final _markers = ValueNotifier<List<AnimatedMarker>>([]);
final _center = const LatLng(51.509364, -0.128928);

final markerSize = 50.0;
final markers = ValueNotifier<List<AnimatedMarker>>([]);
final center = const LatLng(51.509364, -0.128928);

bool _useTransformer = true;

late final _mapController = AnimatedMapController(vsync: this);
late final _animatedMapController = AnimatedMapController(vsync: this);

@override
void dispose() {
_markers.dispose();
_mapController.dispose();
markers.dispose();
_animatedMapController.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: ValueListenableBuilder<List<AnimatedMarker>>(
valueListenable: _markers,
valueListenable: markers,
builder: (context, markers, _) {
return FlutterMap(
mapController: _mapController,
mapController: _animatedMapController.mapController,
options: MapOptions(
center: _center,
center: center,
onTap: (_, point) => _addMarker(point),
),
children: [
Expand All @@ -70,15 +72,15 @@ class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
separator: const SizedBox(height: 8),
children: [
FloatingActionButton(
onPressed: () => _mapController.animatedRotateFrom(
onPressed: () => _animatedMapController.animatedRotateFrom(
90,
customId: _useTransformer ? _useTransformerId : null,
),
tooltip: 'Rotate 90°',
child: const Icon(Icons.rotate_right),
),
FloatingActionButton(
onPressed: () => _mapController.animatedRotateFrom(
onPressed: () => _animatedMapController.animatedRotateFrom(
-90,
customId: _useTransformer ? _useTransformerId : null,
),
Expand All @@ -87,9 +89,9 @@ class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
),
FloatingActionButton(
onPressed: () {
_markers.value = [];
_mapController.animateTo(
dest: _center,
markers.value = [];
_animatedMapController.animateTo(
dest: center,
rotation: 0,
customId: _useTransformer ? _useTransformerId : null,
);
Expand All @@ -98,14 +100,14 @@ class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
child: const Icon(Icons.clear_all),
),
FloatingActionButton(
onPressed: () => _mapController.animatedZoomIn(
onPressed: () => _animatedMapController.animatedZoomIn(
customId: _useTransformer ? _useTransformerId : null,
),
tooltip: 'Zoom in',
child: const Icon(Icons.zoom_in),
),
FloatingActionButton(
onPressed: () => _mapController.animatedZoomOut(
onPressed: () => _animatedMapController.animatedZoomOut(
customId: _useTransformer ? _useTransformerId : null,
),
tooltip: 'Zoom out',
Expand All @@ -114,10 +116,10 @@ class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
FloatingActionButton(
tooltip: 'Center on markers',
onPressed: () {
if (_markers.value.isEmpty) return;
if (markers.value.isEmpty) return;

final points = _markers.value.map((m) => m.point).toList();
_mapController.centerOnPoints(
final points = markers.value.map((m) => m.point).toList();
_animatedMapController.centerOnPoints(
points,
customId: _useTransformer ? _useTransformerId : null,
);
Expand Down Expand Up @@ -152,18 +154,18 @@ class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
}

void _addMarker(LatLng point) {
_markers.value = List.from(_markers.value)
markers.value = List.from(markers.value)
..add(
AnimatedMarker(
point: point,
width: _markerSize,
height: _markerSize,
width: markerSize,
height: markerSize,
anchorPos: AnchorPos.align(AnchorAlign.top),
builder: (context, animation) {
final size = _markerSize * animation.value;
final size = markerSize * animation.value;

return GestureDetector(
onTap: () => _mapController.animateTo(
onTap: () => _animatedMapController.animateTo(
dest: point,
customId: _useTransformer ? _useTransformerId : null,
),
Expand Down
66 changes: 44 additions & 22 deletions lib/src/animated_map_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,43 @@ typedef _MovementCallback = bool Function(
AnimationId animationId,
);

/// A [MapController] that provides animated methods.
class AnimatedMapController extends MapControllerImpl {
/// A wrap around [MapController] that provides animated methods.
class AnimatedMapController {
/// Creates a [MapController] that provides animated methods.
AnimatedMapController({
required this.vsync,
MapController? mapController,
this.duration = const Duration(milliseconds: 500),
this.curve = Curves.fastOutSlowIn,
});
}) : mapController = mapController ?? MapController(),
_internal = mapController == null;

/// The vsync of the animation.
final TickerProvider vsync;

/// Implementation of the map controller that will be used to trigger
/// movements.
///
/// Defaults to a new [MapController] which should be a [MapControllerImpl].
///
/// If created internally (i.e. not passed as a parameter), it will be
/// disposed when [dispose] is called.
final MapController mapController;

/// Whether the map controller was created internally or passed as a
/// parameter. Used to know if the map controller should be disposed or not
/// by the animated map controller.
final bool _internal;

/// The duration of the animation.
final Duration duration;

/// The curve of the animation.
final Curve curve;

/// Current rotation of the map.
///
/// This needs to be overridden because the rotation of the map is not
/// normalized to be between 0° and 360°.
@override
double get rotation {
double effectiveRotation = super.rotation;
double effectiveRotation = mapController.rotation;
if (effectiveRotation >= 360) {
effectiveRotation -= 360;
} else if (effectiveRotation < 0) {
Expand All @@ -51,14 +63,17 @@ class AnimatedMapController extends MapControllerImpl {
/// Controller of the current animation.
AnimationController? _animationController;

@override
void dispose() {
final isAnimating = _animationController?.isAnimating ?? false;
if (isAnimating) {
_animationController?.stop();
}
_animationController?.dispose();
super.dispose();

// Dispose the map controller if it was created internally.
if (_internal) {
mapController.dispose();
}
}

/// Animate the map to [dest] with an optional [zoom] level and [rotation] in
Expand All @@ -85,15 +100,15 @@ class AnimatedMapController extends MapControllerImpl {
);
}

final effectiveDest = dest ?? center;
final effectiveZoom = zoom ?? this.zoom;
final effectiveDest = dest ?? mapController.center;
final effectiveZoom = zoom ?? mapController.zoom;
final effectiveRotation = rotation ?? this.rotation;
final latLngTween = LatLngTween(
begin: center,
begin: mapController.center,
end: effectiveDest,
);
final zoomTween = Tween<double>(
begin: this.zoom,
begin: mapController.zoom,
end: effectiveZoom,
);
double startRotation = this.rotation;
Expand All @@ -117,8 +132,8 @@ class AnimatedMapController extends MapControllerImpl {
// Determine the callback for movement. If no movement will occur return
// immediately.
final bool hasRotation = rotation != null && rotation != this.rotation;
final bool hasMovement =
(dest != null && dest != center) || (zoom != null && zoom != this.zoom);
final bool hasMovement = (dest != null && dest != mapController.center) ||
(zoom != null && zoom != mapController.zoom);
final movementCallback =
_movementCallback(hasMovement: hasMovement, hasRotation: hasRotation);
if (movementCallback == null) return Future.value();
Expand Down Expand Up @@ -184,7 +199,7 @@ class AnimatedMapController extends MapControllerImpl {
}) {
if (hasMovement && hasRotation) {
return (animation, latLngTween, zoomTween, rotateTween, animationId) {
final result = moveAndRotate(
final result = mapController.moveAndRotate(
latLngTween.evaluate(animation),
zoomTween.evaluate(animation),
rotateTween.evaluate(animation),
Expand All @@ -194,14 +209,14 @@ class AnimatedMapController extends MapControllerImpl {
};
} else if (hasMovement) {
return (animation, latLngTween, zoomTween, rotateTween, animationId) =>
move(
mapController.move(
latLngTween.evaluate(animation),
zoomTween.evaluate(animation),
id: animationId.id,
);
} else if (hasRotation) {
return (animation, latLngTween, zoomTween, rotateTween, animationId) =>
rotate(
mapController.rotate(
rotateTween.evaluate(animation),
id: animationId.id,
);
Expand Down Expand Up @@ -259,7 +274,11 @@ class AnimatedMapController extends MapControllerImpl {
///
/// {@macro animated_map_controller.animate_to.curve}
Future<void> animatedZoomIn({Curve? curve, String? customId}) {
return animateTo(zoom: zoom + 1, curve: curve, customId: customId);
return animateTo(
zoom: mapController.zoom + 1,
curve: curve,
customId: customId,
);
}

/// Remove one level to the current zoom level.
Expand All @@ -268,7 +287,7 @@ class AnimatedMapController extends MapControllerImpl {
///
/// {@macro animated_map_controller.animate_to.curve}
FutureOr<void> animatedZoomOut({Curve? curve, String? customId}) {
final newZoom = zoom - 1;
final newZoom = mapController.zoom - 1;
if (newZoom < 0) return null;

return animateTo(zoom: newZoom, curve: curve, customId: customId);
Expand Down Expand Up @@ -301,7 +320,10 @@ class AnimatedMapController extends MapControllerImpl {
}) {
final localOptions =
options ?? const FitBoundsOptions(padding: EdgeInsets.all(12));
final centerZoom = centerZoomFitBounds(bounds, options: localOptions);
final centerZoom = mapController.centerZoomFitBounds(
bounds,
options: localOptions,
);

return animateTo(
dest: centerZoom.center,
Expand Down

0 comments on commit fcb93fa

Please sign in to comment.