blob: 5690cddcbe06ed06700b55eda3a1aac05a1c909e [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
part of google_maps_flutter;
dynamic _offsetToJson(Offset offset) {
if (offset == null) {
return null;
}
return <dynamic>[offset.dx, offset.dy];
}
/// Text labels for a [Marker] info window.
class InfoWindow {
/// Creates an immutable representation of a label on for [Marker].
const InfoWindow({
this.title,
this.snippet,
this.anchor = const Offset(0.5, 0.0),
this.onTap,
});
/// Text labels specifying that no text is to be displayed.
static const InfoWindow noText = InfoWindow();
/// Text displayed in an info window when the user taps the marker.
///
/// A null value means no title.
final String title;
/// Additional text displayed below the [title].
///
/// A null value means no additional text.
final String snippet;
/// The icon image point that will be the anchor of the info window when
/// displayed.
///
/// The image point is specified in normalized coordinates: An anchor of
/// (0.0, 0.0) means the top left corner of the image. An anchor
/// of (1.0, 1.0) means the bottom right corner of the image.
final Offset anchor;
/// onTap callback for this [InfoWindow].
final VoidCallback onTap;
/// Creates a new [InfoWindow] object whose values are the same as this instance,
/// unless overwritten by the specified parameters.
InfoWindow copyWith({
String titleParam,
String snippetParam,
Offset anchorParam,
VoidCallback onTapParam,
}) {
return InfoWindow(
title: titleParam ?? title,
snippet: snippetParam ?? snippet,
anchor: anchorParam ?? anchor,
onTap: onTapParam ?? onTap,
);
}
dynamic _toJson() {
final Map<String, dynamic> json = <String, dynamic>{};
void addIfPresent(String fieldName, dynamic value) {
if (value != null) {
json[fieldName] = value;
}
}
addIfPresent('title', title);
addIfPresent('snippet', snippet);
addIfPresent('anchor', _offsetToJson(anchor));
return json;
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other.runtimeType != runtimeType) return false;
final InfoWindow typedOther = other;
return title == typedOther.title &&
snippet == typedOther.snippet &&
anchor == typedOther.anchor;
}
@override
int get hashCode => hashValues(title.hashCode, snippet, anchor);
@override
String toString() {
return 'InfoWindow{title: $title, snippet: $snippet, anchor: $anchor}';
}
}
/// Uniquely identifies a [Marker] among [GoogleMap] markers.
///
/// This does not have to be globally unique, only unique among the list.
@immutable
class MarkerId {
/// Creates an immutable identifier for a [Marker].
MarkerId(this.value) : assert(value != null);
/// value of the [MarkerId].
final String value;
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other.runtimeType != runtimeType) return false;
final MarkerId typedOther = other;
return value == typedOther.value;
}
@override
int get hashCode => value.hashCode;
@override
String toString() {
return 'MarkerId{value: $value}';
}
}
/// Marks a geographical location on the map.
///
/// A marker icon is drawn oriented against the device's screen rather than
/// the map's surface; that is, it will not necessarily change orientation
/// due to map rotations, tilting, or zooming.
@immutable
class Marker {
/// Creates a set of marker configuration options.
///
/// Default marker options.
///
/// Specifies a marker that
/// * is fully opaque; [alpha] is 1.0
/// * uses icon bottom center to indicate map position; [anchor] is (0.5, 1.0)
/// * has default tap handling; [consumeTapEvents] is false
/// * is stationary; [draggable] is false
/// * is drawn against the screen, not the map; [flat] is false
/// * has a default icon; [icon] is `BitmapDescriptor.defaultMarker`
/// * anchors the info window at top center; [infoWindowAnchor] is (0.5, 0.0)
/// * has no info window text; [infoWindowText] is `InfoWindowText.noText`
/// * is positioned at 0, 0; [position] is `LatLng(0.0, 0.0)`
/// * has an axis-aligned icon; [rotation] is 0.0
/// * is visible; [visible] is true
/// * is placed at the base of the drawing order; [zIndex] is 0.0
/// * reports [onTap] events
/// * reports [onDragEnd] events
const Marker({
@required this.markerId,
this.alpha = 1.0,
this.anchor = const Offset(0.5, 1.0),
this.consumeTapEvents = false,
this.draggable = false,
this.flat = false,
this.icon = BitmapDescriptor.defaultMarker,
this.infoWindow = InfoWindow.noText,
this.position = const LatLng(0.0, 0.0),
this.rotation = 0.0,
this.visible = true,
this.zIndex = 0.0,
this.onTap,
this.onDragEnd,
}) : assert(alpha == null || (0.0 <= alpha && alpha <= 1.0));
/// Uniquely identifies a [Marker].
final MarkerId markerId;
/// The opacity of the marker, between 0.0 and 1.0 inclusive.
///
/// 0.0 means fully transparent, 1.0 means fully opaque.
final double alpha;
/// The icon image point that will be placed at the [position] of the marker.
///
/// The image point is specified in normalized coordinates: An anchor of
/// (0.0, 0.0) means the top left corner of the image. An anchor
/// of (1.0, 1.0) means the bottom right corner of the image.
final Offset anchor;
/// True if the marker icon consumes tap events. If not, the map will perform
/// default tap handling by centering the map on the marker and displaying its
/// info window.
final bool consumeTapEvents;
/// True if the marker is draggable by user touch events.
final bool draggable;
/// True if the marker is rendered flatly against the surface of the Earth, so
/// that it will rotate and tilt along with map camera movements.
final bool flat;
/// A description of the bitmap used to draw the marker icon.
final BitmapDescriptor icon;
/// A Google Maps InfoWindow.
///
/// The window is displayed when the marker is tapped.
final InfoWindow infoWindow;
/// Geographical location of the marker.
final LatLng position;
/// Rotation of the marker image in degrees clockwise from the [anchor] point.
final double rotation;
/// True if the marker is visible.
final bool visible;
/// The z-index of the marker, used to determine relative drawing order of
/// map overlays.
///
/// Overlays are drawn in order of z-index, so that lower values means drawn
/// earlier, and thus appearing to be closer to the surface of the Earth.
final double zIndex;
/// Callbacks to receive tap events for markers placed on this map.
final VoidCallback onTap;
/// Signature reporting the new [LatLng] at the end of a drag event.
final ValueChanged<LatLng> onDragEnd;
/// Creates a new [Marker] object whose values are the same as this instance,
/// unless overwritten by the specified parameters.
Marker copyWith({
double alphaParam,
Offset anchorParam,
bool consumeTapEventsParam,
bool draggableParam,
bool flatParam,
BitmapDescriptor iconParam,
InfoWindow infoWindowParam,
LatLng positionParam,
double rotationParam,
bool visibleParam,
double zIndexParam,
VoidCallback onTapParam,
ValueChanged<LatLng> onDragEndParam,
}) {
return Marker(
markerId: markerId,
alpha: alphaParam ?? alpha,
anchor: anchorParam ?? anchor,
consumeTapEvents: consumeTapEventsParam ?? consumeTapEvents,
draggable: draggableParam ?? draggable,
flat: flatParam ?? flat,
icon: iconParam ?? icon,
infoWindow: infoWindowParam ?? infoWindow,
position: positionParam ?? position,
rotation: rotationParam ?? rotation,
visible: visibleParam ?? visible,
zIndex: zIndexParam ?? zIndex,
onTap: onTapParam ?? onTap,
onDragEnd: onDragEndParam ?? onDragEnd,
);
}
/// Creates a new [Marker] object whose values are the same as this instance.
Marker clone() => copyWith();
Map<String, dynamic> _toJson() {
final Map<String, dynamic> json = <String, dynamic>{};
void addIfPresent(String fieldName, dynamic value) {
if (value != null) {
json[fieldName] = value;
}
}
addIfPresent('markerId', markerId.value);
addIfPresent('alpha', alpha);
addIfPresent('anchor', _offsetToJson(anchor));
addIfPresent('consumeTapEvents', consumeTapEvents);
addIfPresent('draggable', draggable);
addIfPresent('flat', flat);
addIfPresent('icon', icon?._toJson());
addIfPresent('infoWindow', infoWindow?._toJson());
addIfPresent('position', position?._toJson());
addIfPresent('rotation', rotation);
addIfPresent('visible', visible);
addIfPresent('zIndex', zIndex);
return json;
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
if (other.runtimeType != runtimeType) return false;
final Marker typedOther = other;
return markerId == typedOther.markerId &&
alpha == typedOther.alpha &&
anchor == typedOther.anchor &&
consumeTapEvents == typedOther.consumeTapEvents &&
draggable == typedOther.draggable &&
flat == typedOther.flat &&
icon == typedOther.icon &&
infoWindow == typedOther.infoWindow &&
position == typedOther.position &&
rotation == typedOther.rotation &&
visible == typedOther.visible &&
zIndex == typedOther.zIndex &&
onTap == typedOther.onTap;
}
@override
int get hashCode => markerId.hashCode;
@override
String toString() {
return 'Marker{markerId: $markerId, alpha: $alpha, anchor: $anchor, '
'consumeTapEvents: $consumeTapEvents, draggable: $draggable, flat: $flat, '
'icon: $icon, infoWindow: $infoWindow, position: $position, rotation: $rotation, '
'visible: $visible, zIndex: $zIndex, onTap: $onTap}';
}
}
Map<MarkerId, Marker> _keyByMarkerId(Iterable<Marker> markers) {
if (markers == null) {
return <MarkerId, Marker>{};
}
return Map<MarkerId, Marker>.fromEntries(markers.map((Marker marker) =>
MapEntry<MarkerId, Marker>(marker.markerId, marker.clone())));
}
List<Map<String, dynamic>> _serializeMarkerSet(Set<Marker> markers) {
if (markers == null) {
return null;
}
return markers.map<Map<String, dynamic>>((Marker m) => m._toJson()).toList();
}