Skip to content

Commit

Permalink
[google_maps_flutter_platform_interface] Split CameraUpdate into de…
Browse files Browse the repository at this point in the history
…rived classes to use structured data (#7596)

Platform interface part of #7507

flutter/flutter#152928

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] page, which explains my
responsibilities.
- [x] I read and followed the [relevant style guides] and ran the
auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages
repo does use `dart format`.)
- [ ] I signed the [CLA].
- [x] The title of the PR starts with the name of the package surrounded
by square brackets, e.g. `[shared_preferences]`
- [x] I [linked to at least one issue that this PR fixes] in the
description above.
- [x] I updated `pubspec.yaml` with an appropriate new version according
to the [pub versioning philosophy], or this PR is [exempt from version
changes].
- [x] I updated `CHANGELOG.md` to add a description of the change,
[following repository CHANGELOG style], or this PR is [exempt from
CHANGELOG changes].
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/packages/blob/main/CONTRIBUTING.md
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md
[relevant style guides]:
https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style
[CLA]: https://cla.developers.google.com/
[Discord]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Chat.md
[linked to at least one issue that this PR fixes]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview
[pub versioning philosophy]: https://dart.dev/tools/pub/versioning
[exempt from version changes]:
https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#version
[following repository CHANGELOG style]:
https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changelog-style
[exempt from CHANGELOG changes]:
https://github.com/flutter/flutter/blob/master/docs/ecosystem/contributing/README.md#changelog
[test-exempt]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#tests
  • Loading branch information
yaakovschectman authored Sep 9, 2024
1 parent 8d1acdd commit d07c025
Show file tree
Hide file tree
Showing 4 changed files with 243 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 2.9.1

* Splits CameraUpdate into dervied classes for different update cases.
* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3.

## 2.9.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,42 +110,67 @@ class CameraPosition {
'CameraPosition(bearing: $bearing, target: $target, tilt: $tilt, zoom: $zoom)';
}

/// Indicates which type of camera update this instance represents.
enum CameraUpdateType {
/// New position for camera
newCameraPosition,

/// New coordinates for camera
newLatLng,

/// New coordinates bounding box
newLatLngBounds,

/// New coordinate with zoom level
newLatLngZoom,

/// Move by a scroll delta
scrollBy,

/// Zoom by a relative change
zoomBy,

/// Zoom to an absolute level
zoomTo,

/// Zoom in
zoomIn,

/// Zoom out
zoomOut,
}

/// Defines a camera move, supporting absolute moves as well as moves relative
/// the current position.
class CameraUpdate {
const CameraUpdate._(this._json);
abstract class CameraUpdate {
const CameraUpdate._(this.updateType);

/// Indicates which type of camera update this instance represents.
final CameraUpdateType updateType;

/// Returns a camera update that moves the camera to the specified position.
static CameraUpdate newCameraPosition(CameraPosition cameraPosition) {
return CameraUpdate._(
<Object>['newCameraPosition', cameraPosition.toMap()],
);
return CameraUpdateNewCameraPosition(cameraPosition);
}

/// Returns a camera update that moves the camera target to the specified
/// geographical location.
static CameraUpdate newLatLng(LatLng latLng) {
return CameraUpdate._(<Object>['newLatLng', latLng.toJson()]);
return CameraUpdateNewLatLng(latLng);
}

/// Returns a camera update that transforms the camera so that the specified
/// geographical bounding box is centered in the map view at the greatest
/// possible zoom level. A non-zero [padding] insets the bounding box from the
/// map view's edges. The camera's new tilt and bearing will both be 0.0.
static CameraUpdate newLatLngBounds(LatLngBounds bounds, double padding) {
return CameraUpdate._(<Object>[
'newLatLngBounds',
bounds.toJson(),
padding,
]);
return CameraUpdateNewLatLngBounds(bounds, padding);
}

/// Returns a camera update that moves the camera target to the specified
/// geographical location and zoom level.
static CameraUpdate newLatLngZoom(LatLng latLng, double zoom) {
return CameraUpdate._(
<Object>['newLatLngZoom', latLng.toJson(), zoom],
);
return CameraUpdateNewLatLngZoom(latLng, zoom);
}

/// Returns a camera update that moves the camera target the specified screen
Expand All @@ -155,49 +180,154 @@ class CameraUpdate {
/// the camera's target to a geographical location that is 50 to the east and
/// 75 to the south of the current location, measured in screen coordinates.
static CameraUpdate scrollBy(double dx, double dy) {
return CameraUpdate._(
<Object>['scrollBy', dx, dy],
);
return CameraUpdateScrollBy(dx, dy);
}

/// Returns a camera update that modifies the camera zoom level by the
/// specified amount. The optional [focus] is a screen point whose underlying
/// geographical location should be invariant, if possible, by the movement.
static CameraUpdate zoomBy(double amount, [Offset? focus]) {
if (focus == null) {
return CameraUpdate._(<Object>['zoomBy', amount]);
} else {
return CameraUpdate._(<Object>[
'zoomBy',
amount,
<double>[focus.dx, focus.dy],
]);
}
return CameraUpdateZoomBy(amount, focus);
}

/// Returns a camera update that zooms the camera in, bringing the camera
/// closer to the surface of the Earth.
///
/// Equivalent to the result of calling `zoomBy(1.0)`.
static CameraUpdate zoomIn() {
return const CameraUpdate._(<Object>['zoomIn']);
return const CameraUpdateZoomIn();
}

/// Returns a camera update that zooms the camera out, bringing the camera
/// further away from the surface of the Earth.
///
/// Equivalent to the result of calling `zoomBy(-1.0)`.
static CameraUpdate zoomOut() {
return const CameraUpdate._(<Object>['zoomOut']);
return const CameraUpdateZoomOut();
}

/// Returns a camera update that sets the camera zoom level.
static CameraUpdate zoomTo(double zoom) {
return CameraUpdate._(<Object>['zoomTo', zoom]);
return CameraUpdateZoomTo(zoom);
}

final Object _json;

/// Converts this object to something serializable in JSON.
Object toJson() => _json;
Object toJson();
}

/// Defines a camera move to a new position.
class CameraUpdateNewCameraPosition extends CameraUpdate {
/// Creates a camera move.
const CameraUpdateNewCameraPosition(this.cameraPosition)
: super._(CameraUpdateType.newCameraPosition);

/// The new camera position.
final CameraPosition cameraPosition;
@override
Object toJson() => <Object>['newCameraPosition', cameraPosition.toMap()];
}

/// Defines a camera move to a latitude and longitude.
class CameraUpdateNewLatLng extends CameraUpdate {
/// Creates a camera move to latitude and longitude.
const CameraUpdateNewLatLng(this.latLng)
: super._(CameraUpdateType.newLatLng);

/// New latitude and longitude of the camera..
final LatLng latLng;
@override
Object toJson() => <Object>['newLatLng', latLng.toJson()];
}

/// Defines a camera move to a new bounding latitude and longitude range.
class CameraUpdateNewLatLngBounds extends CameraUpdate {
/// Creates a camera move to a bounding range.
const CameraUpdateNewLatLngBounds(this.bounds, this.padding)
: super._(CameraUpdateType.newLatLngBounds);

/// The northeast and southwest bounding coordinates.
final LatLngBounds bounds;

/// The amount of padding by which the view is inset.
final double padding;
@override
Object toJson() => <Object>['newLatLngZoom', bounds.toJson(), padding];
}

/// Defines a camera move to new coordinates with a zoom level.
class CameraUpdateNewLatLngZoom extends CameraUpdate {
/// Creates a camera move with coordinates and zoom level.
const CameraUpdateNewLatLngZoom(this.latLng, this.zoom)
: super._(CameraUpdateType.newLatLngZoom);

/// New coordinates of the camera.
final LatLng latLng;

/// New zoom level of the camera.
final double zoom;
@override
Object toJson() => <Object>['newLatLngZoom', latLng.toJson(), zoom];
}

/// Defines a camera scroll by a certain delta.
class CameraUpdateScrollBy extends CameraUpdate {
/// Creates a camera scroll.
const CameraUpdateScrollBy(this.dx, this.dy)
: super._(CameraUpdateType.scrollBy);

/// Scroll delta x.
final double dx;

/// Scroll delta y.
final double dy;
@override
Object toJson() => <Object>['scrollBy', dx, dy];
}

/// Defines a relative camera zoom.
class CameraUpdateZoomBy extends CameraUpdate {
/// Creates a relative camera zoom.
const CameraUpdateZoomBy(this.amount, [this.focus])
: super._(CameraUpdateType.zoomBy);

/// Change in camera zoom amount.
final double amount;

/// Optional point around which the zoom is focused.
final Offset? focus;
@override
Object toJson() => (focus == null)
? <Object>['zoomBy', amount]
: <Object>[
'zoomBy',
amount,
<double>[focus!.dx, focus!.dy]
];
}

/// Defines a camera zoom in.
class CameraUpdateZoomIn extends CameraUpdate {
/// Zooms in the camera.
const CameraUpdateZoomIn() : super._(CameraUpdateType.zoomIn);
@override
Object toJson() => <Object>['zoomIn'];
}

/// Defines a camera zoom out.
class CameraUpdateZoomOut extends CameraUpdate {
/// Zooms out the camera.
const CameraUpdateZoomOut() : super._(CameraUpdateType.zoomOut);
@override
Object toJson() => <Object>['zoomOut'];
}

/// Defines a camera zoom to an absolute zoom.
class CameraUpdateZoomTo extends CameraUpdate {
/// Creates a zoom to an absolute zoom level.
const CameraUpdateZoomTo(this.zoom) : super._(CameraUpdateType.zoomTo);

/// New zoom level of the camera.
final double zoom;
@override
Object toJson() => <Object>['zoomTo', zoom];
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ repository: https://github.com/flutter/packages/tree/main/packages/google_maps_f
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+maps%22
# NOTE: We strongly prefer non-breaking changes, even at the expense of a
# less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
version: 2.9.0
version: 2.9.1

environment:
sdk: ^3.3.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,82 @@ void main() {

expect(cameraPosition, cameraPositionFromJson);
});

test('CameraUpdate.newCameraPosition', () {
const CameraPosition cameraPosition = CameraPosition(
target: LatLng(10.0, 15.0), bearing: 0.5, tilt: 30.0, zoom: 1.5);
final CameraUpdate cameraUpdate =
CameraUpdate.newCameraPosition(cameraPosition);
expect(cameraUpdate.runtimeType, CameraUpdateNewCameraPosition);
expect(cameraUpdate.updateType, CameraUpdateType.newCameraPosition);
cameraUpdate as CameraUpdateNewCameraPosition;
expect(cameraUpdate.cameraPosition, cameraPosition);
});

test('CameraUpdate.newLatLng', () {
const LatLng latLng = LatLng(1.0, 2.0);
final CameraUpdate cameraUpdate = CameraUpdate.newLatLng(latLng);
expect(cameraUpdate.runtimeType, CameraUpdateNewLatLng);
expect(cameraUpdate.updateType, CameraUpdateType.newLatLng);
cameraUpdate as CameraUpdateNewLatLng;
expect(cameraUpdate.latLng, latLng);
});

test('CameraUpdate.newLatLngBounds', () {
final LatLngBounds latLngBounds = LatLngBounds(
northeast: const LatLng(1.0, 2.0), southwest: const LatLng(-2.0, -3.0));
const double padding = 1.0;
final CameraUpdate cameraUpdate =
CameraUpdate.newLatLngBounds(latLngBounds, padding);
expect(cameraUpdate.runtimeType, CameraUpdateNewLatLngBounds);
expect(cameraUpdate.updateType, CameraUpdateType.newLatLngBounds);
cameraUpdate as CameraUpdateNewLatLngBounds;
expect(cameraUpdate.bounds, latLngBounds);
expect(cameraUpdate.padding, padding);
});

test('CameraUpdate.newLatLngZoom', () {
const LatLng latLng = LatLng(1.0, 2.0);
const double zoom = 2.0;
final CameraUpdate cameraUpdate = CameraUpdate.newLatLngZoom(latLng, zoom);
expect(cameraUpdate.runtimeType, CameraUpdateNewLatLngZoom);
expect(cameraUpdate.updateType, CameraUpdateType.newLatLngZoom);
cameraUpdate as CameraUpdateNewLatLngZoom;
expect(cameraUpdate.latLng, latLng);
expect(cameraUpdate.zoom, zoom);
});

test('CameraUpdate.scrollBy', () {
const double dx = 2.0;
const double dy = 5.0;
final CameraUpdate cameraUpdate = CameraUpdate.scrollBy(dx, dy);
expect(cameraUpdate.runtimeType, CameraUpdateScrollBy);
expect(cameraUpdate.updateType, CameraUpdateType.scrollBy);
cameraUpdate as CameraUpdateScrollBy;
expect(cameraUpdate.dx, dx);
expect(cameraUpdate.dy, dy);
});

test('CameraUpdate.zoomBy', () {
const double amount = 1.5;
const Offset focus = Offset(-1.0, -2.0);
final CameraUpdate cameraUpdate = CameraUpdate.zoomBy(amount, focus);
expect(cameraUpdate.runtimeType, CameraUpdateZoomBy);
expect(cameraUpdate.updateType, CameraUpdateType.zoomBy);
cameraUpdate as CameraUpdateZoomBy;
expect(cameraUpdate.amount, amount);
expect(cameraUpdate.focus, focus);
});

test('CameraUpdate.zoomIn', () {
final CameraUpdate cameraUpdate = CameraUpdate.zoomIn();
expect(cameraUpdate.runtimeType, CameraUpdateZoomIn);
expect(cameraUpdate.updateType, CameraUpdateType.zoomIn);
});

test('CameraUpdate.zoomOut', () {
final CameraUpdate cameraUpdate = CameraUpdate.zoomOut();
expect(cameraUpdate.runtimeType, CameraUpdateZoomOut);
expect(cameraUpdate.updateType, CameraUpdateType.zoomOut);
});
}

0 comments on commit d07c025

Please sign in to comment.