Skip to content
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

feat: ui widget enhancements #217

Merged
merged 5 commits into from
Feb 13, 2025
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
2 changes: 0 additions & 2 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import 'package:maplibre_example/style_layers_raster_page.dart';
import 'package:maplibre_example/style_layers_symbol_page.dart';
import 'package:maplibre_example/styled_map_page.dart';
import 'package:maplibre_example/two_maps_page.dart';
import 'package:maplibre_example/user_interface_page.dart';
import 'package:maplibre_example/user_location_page.dart';
import 'package:maplibre_example/widget_layer_page.dart';

Expand Down Expand Up @@ -52,7 +51,6 @@ class MyApp extends StatelessWidget {
EventsPage.location: (context) => const EventsPage(),
StyledMapPage.location: (context) => const StyledMapPage(),
UserLocationPage.location: (context) => const UserLocationPage(),
UserInterfacePage.location: (context) => const UserInterfacePage(),
WidgetLayerPage.location: (context) => const WidgetLayerPage(),
OfflinePage.location: (context) => const OfflinePage(),
PermissionsPage.location: (context) => const PermissionsPage(),
Expand Down
6 changes: 0 additions & 6 deletions example/lib/menu_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import 'package:maplibre_example/style_layers_raster_page.dart';
import 'package:maplibre_example/style_layers_symbol_page.dart';
import 'package:maplibre_example/styled_map_page.dart';
import 'package:maplibre_example/two_maps_page.dart';
import 'package:maplibre_example/user_interface_page.dart';
import 'package:maplibre_example/user_location_page.dart';
import 'package:maplibre_example/widget_layer_page.dart';

Expand Down Expand Up @@ -83,11 +82,6 @@ class MenuPage extends StatelessWidget {
iconData: Icons.gps_fixed,
location: UserLocationPage.location,
),
ItemCard(
label: 'User interface',
iconData: Icons.control_camera,
location: UserInterfacePage.location,
),
if (!kIsWeb)
ItemCard(
label: 'Offline',
Expand Down
7 changes: 6 additions & 1 deletion example/lib/styled_map_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ class _StyledMapPageState extends State<StyledMapPage> {
initZoom: 2,
initStyle: MapStyles.maptilerStreets,
),
children: const [SourceAttribution()],
children: const [
MapScalebar(),
SourceAttribution(),
MapControlButtons(showTrackLocation: true),
MapCompass(),
],
onStyleLoaded: (style) {
style.setProjection(MapProjection.globe);
},
Expand Down
39 changes: 0 additions & 39 deletions example/lib/user_interface_page.dart

This file was deleted.

6 changes: 6 additions & 0 deletions lib/src/map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ class MapLibreMap extends StatefulWidget {

/// Flutter widgets that get displayed on top on the map and are within the
/// [MapLibreMap] context.
///
/// You can use the following included UI elements:
/// - [MapCompass]
/// - [MapControlButtons]
/// - [MapScalebar]
/// - [SourceAttribution].
final List<Widget> children;

/// Which gestures should be consumed by the map.
Expand Down
17 changes: 17 additions & 0 deletions lib/src/platform/web/interop/camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,23 @@ extension type FlyToOptions._(CameraOptions _) implements CameraOptions {
});
}

/// Options to specify the map bounds.
@anonymous
@JS()
extension type FitBoundsOptions._(FlyToOptions _) implements FlyToOptions {
/// Create a new JS [FitBoundsOptions] object.
external FitBoundsOptions({
bool? linear,
Point? offset,
num? maxZoom,
num? maxDuration,
PaddingOptions? padding,
num? speed,
num? pitch,
num? bearing,
});
}

/// https://github.com/maplibre/maplibre-gl-js/blob/8a005bd7d4769b62db470ce7e8cf2b08255c1d36/src/geo/edge_insets.ts#L129
@anonymous
@JS()
Expand Down
17 changes: 0 additions & 17 deletions lib/src/platform/web/interop/map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,23 +136,6 @@ extension type MapOptions._(JSObject _) implements JSObject {
});
}

/// Options to specify the map bounds.
@anonymous
@JS()
extension type FitBoundsOptions._(FlyToOptions _) implements FlyToOptions {
/// Create a new JS [FitBoundsOptions] object.
external FitBoundsOptions({
bool? linear,
Point? offset,
num? maxZoom,
num? maxDuration,
PaddingOptions? padding,
num? speed,
num? pitch,
num? bearing,
});
}

/// The specifications of map sources.
@anonymous
@JS()
Expand Down
44 changes: 24 additions & 20 deletions lib/src/platform/web/map_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -243,12 +243,13 @@ final class MapLibreMapStateWeb extends MapLibreMapState {
double? pitch,
}) async {
_nextGestureCausedByController = true;
final camera = getCamera();
_map.jumpTo(
interop.JumpToOptions(
center: center?.toLngLat(),
zoom: zoom,
bearing: bearing,
pitch: pitch,
zoom: zoom ?? camera.zoom,
bearing: bearing ?? camera.bearing,
pitch: pitch ?? camera.pitch,
),
);
}
Expand All @@ -265,12 +266,13 @@ final class MapLibreMapStateWeb extends MapLibreMapState {
}) async {
final destination = center?.toLngLat();
_nextGestureCausedByController = true;
final camera = getCamera();
_map.flyTo(
interop.FlyToOptions(
center: destination,
zoom: zoom,
bearing: bearing,
pitch: pitch,
zoom: zoom ?? camera.zoom,
bearing: bearing ?? camera.bearing,
pitch: pitch ?? camera.pitch,
speed: webSpeed,
maxDuration: webMaxDuration?.inMilliseconds,
),
Expand Down Expand Up @@ -313,20 +315,22 @@ final class MapLibreMapStateWeb extends MapLibreMapState {
double webMaxZoom = double.maxFinite,
bool webLinear = false,
EdgeInsets padding = EdgeInsets.zero,
}) async =>
_map.fitBounds(
bounds.toJsLngLatBounds(),
interop.FitBoundsOptions(
offset: offset.toJsPoint(),
maxZoom: webMaxZoom,
linear: webLinear,
maxDuration: webMaxDuration?.inMilliseconds,
padding: padding.toPaddingOptions(),
speed: webSpeed,
pitch: pitch,
bearing: bearing,
),
);
}) async {
final camera = getCamera();
_map.fitBounds(
bounds.toJsLngLatBounds(),
interop.FitBoundsOptions(
offset: offset.toJsPoint(),
maxZoom: webMaxZoom,
linear: webLinear,
maxDuration: webMaxDuration?.inMilliseconds,
padding: padding.toPaddingOptions(),
speed: webSpeed,
pitch: pitch ?? camera.pitch,
bearing: bearing ?? camera.bearing,
),
);
}

@override
MapCamera getCamera() => MapCamera(
Expand Down
32 changes: 23 additions & 9 deletions lib/src/ui/map_compass.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class MapCompass extends StatelessWidget {
super.key,
this.rotationOffset = 0,
this.rotationDuration = const Duration(milliseconds: 200),
this.webRotationSpeed = 1.2,
this.removePinchOnPressed = false,
this.rotateNorthOnPressed = true,
this.onPressed,
this.hideIfRotatedNorth = false,
Expand All @@ -40,6 +42,11 @@ class MapCompass extends StatelessWidget {
/// Defaults to true.
final bool rotateNorthOnPressed;

/// Reset the camera pinch / tilt back to 0 when clicked.
///
/// Defaults to false.
final bool removePinchOnPressed;

/// Set to true to hide the compass while the map is not rotated.
///
/// Defaults to false (always visible).
Expand All @@ -60,6 +67,9 @@ class MapCompass extends StatelessWidget {
/// Default to 200 ms.
final Duration rotationDuration;

/// The speed of the rotation animation on web.
final double webRotationSpeed;

/// The compass radius.
final double radius;

Expand All @@ -80,15 +90,7 @@ class MapCompass extends StatelessWidget {
angle: (-camera.bearing + rotationOffset) * degree2Radian,
child: PointerInterceptor(
child: InkWell(
onTap: () {
if (rotateNorthOnPressed) {
controller.animateCamera(
bearing: 0,
nativeDuration: rotationDuration,
);
}
onPressed?.call();
},
onTap: () => _onTap(controller),
child: child ??
CustomPaint(
painter: _CompassPainter(radius: radius),
Expand All @@ -99,6 +101,18 @@ class MapCompass extends StatelessWidget {
),
);
}

void _onTap(MapController controller) {
if (rotateNorthOnPressed || removePinchOnPressed) {
controller.animateCamera(
bearing: rotateNorthOnPressed ? 0 : null,
pitch: removePinchOnPressed ? 0 : null,
nativeDuration: rotationDuration,
webSpeed: webRotationSpeed,
);
}
onPressed?.call();
}
}

class _CompassPainter extends CustomPainter {
Expand Down
3 changes: 1 addition & 2 deletions lib/src/ui/map_control_buttons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class _MapControlButtonsState extends State<MapControlButtons> {
padding: widget.padding,
child: PointerInterceptor(
child: Column(
spacing: 8,
mainAxisSize: MainAxisSize.min,
children: [
FloatingActionButton(
Expand All @@ -87,7 +88,6 @@ class _MapControlButtonsState extends State<MapControlButtons> {
),
child: const Icon(Icons.add),
),
const SizedBox(height: 8),
FloatingActionButton(
heroTag: 'MapLibreZoomOutButton',
onPressed: () => controller.animateCamera(
Expand All @@ -97,7 +97,6 @@ class _MapControlButtonsState extends State<MapControlButtons> {
child: const Icon(Icons.remove),
),
if (!kIsWeb && widget.showTrackLocation) ...[
const SizedBox(height: 8),
FloatingActionButton(
heroTag: 'MapLibreTrackLocationButton',
onPressed: () async => _initializeLocation(controller),
Expand Down
Loading
Loading