Skip to content

[google_maps_flutter_platform_interface] Split CameraUpdate into derived classes to use structured data #7596

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Sep 9, 2024
Merged
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
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];
Copy link
Contributor

Choose a reason for hiding this comment

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

@yaakovschectman, @stuartmorgan
This should have been 'newLatLngBounds' instead of 'newLatLngZoom'

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for flagging that; I've retracted 2.9.1 on pub.dev. I assumed we already had Dart unit testing of the JSON we sent, since we usually do, but apparently not in this case.

@yaakovschectman You'll need to fix this so we can publish a new non-retracted version of the platform interface to unblock your other PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added you both as reviewers to a PR that addresses this fix.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why can't I find the new version (2.9.1) of the google_maps_flutter package in pub.dev ?

Copy link
Contributor Author

@yaakovschectman yaakovschectman Sep 12, 2024

Choose a reason for hiding this comment

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

@dhiaCodes The updated package is google_maps_flutter_platform_interface, a dependency of google_maps_flutter.

}

/// 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);
});
}