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!: enhance Map control #3994

Merged
merged 15 commits into from
Oct 19, 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
36 changes: 34 additions & 2 deletions client/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.10"
dio:
dependency: transitive
description:
name: dio
sha256: "5598aa796bbf4699afd5c67c0f5f6e2ed542afc956884b9cd58c306966efc260"
url: "https://pub.dev"
source: hosted
version: "5.7.0"
dio_web_adapter:
dependency: transitive
description:
name: dio_web_adapter
sha256: "33259a9276d6cea88774a0000cfae0d861003497755969c92faa223108620dc8"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
equatable:
dependency: transitive
description:
Expand Down Expand Up @@ -349,6 +365,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.2"
flutter_map_animations:
dependency: transitive
description:
name: flutter_map_animations
sha256: a2135cd3cf36c07d821efeabb0be31aca380449528af80913c77b158e142eae9
url: "https://pub.dev"
source: hosted
version: "0.7.1"
flutter_map_cancellable_tile_provider:
dependency: transitive
description:
name: flutter_map_cancellable_tile_provider
sha256: "03662220ce0cd784ad2f2a45c36fc379b8b315c74f5c12b5ff4a0515eab1acd1"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
flutter_markdown:
dependency: transitive
description:
Expand Down Expand Up @@ -1322,10 +1354,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
url: "https://pub.dev"
source: hosted
version: "14.2.5"
version: "14.2.4"
volume_controller:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions packages/flet/lib/flet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,4 @@ export 'src/utils/text.dart';
export 'src/utils/textfield.dart';
export 'src/utils/theme.dart';
export 'src/utils/time.dart';
export 'src/utils/transforms.dart';
2 changes: 1 addition & 1 deletion packages/flet/lib/src/utils/time.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Duration? parseDuration(Control control, String propName,
[Duration? defaultValue]) {
var v = control.attrString(propName, null);
if (v == null) {
return null;
return defaultValue;
}

final j1 = json.decode(v);
Expand Down
5 changes: 1 addition & 4 deletions packages/flet_map/lib/src/circle_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@ import 'utils/map.dart';
class CircleLayerControl extends StatelessWidget with FletStoreMixin {
final Control? parent;
final Control control;
final List<Control> children;

const CircleLayerControl(
{super.key,
required this.parent,
required this.control,
required this.children});
required this.parent, required this.control});

@override
Widget build(BuildContext context) {
Expand Down
4 changes: 0 additions & 4 deletions packages/flet_map/lib/src/create_control.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,12 @@ CreateControlFactory createControl = (CreateControlArgs args) {
return RichAttributionControl(
parent: args.parent,
control: args.control,
children: args.children,
backend: args.backend,
);
case "map_simple_attribution":
return SimpleAttributionControl(
parent: args.parent,
control: args.control,
children: args.children,
backend: args.backend,
);
case "map_tile_layer":
Expand All @@ -50,13 +48,11 @@ CreateControlFactory createControl = (CreateControlArgs args) {
return CircleLayerControl(
parent: args.parent,
control: args.control,
children: args.children,
);
case "map_polygon_layer":
return PolygonLayerControl(
parent: args.parent,
control: args.control,
children: args.children,
);
case "map_polyline_layer":
return PolylineLayerControl(
Expand Down
243 changes: 96 additions & 147 deletions packages/flet_map/lib/src/map.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import 'dart:convert';

import 'package:flet/flet.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map_animations/flutter_map_animations.dart';
import 'package:latlong2/latlong.dart';

import 'utils/map.dart';
Expand All @@ -26,12 +27,26 @@ class MapControl extends StatefulWidget {
State<MapControl> createState() => _MapControlState();
}

class _MapControlState extends State<MapControl> with FletStoreMixin {
class _MapControlState extends State<MapControl>
with FletStoreMixin, TickerProviderStateMixin {
late final _animatedMapController = AnimatedMapController(vsync: this);

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

Duration? durationFromString(String? duration, [Duration? defaultValue]) {
return duration != null
? durationFromJSON(json.decode(duration), defaultValue)
: defaultValue;
}

@override
Widget build(BuildContext context) {
debugPrint("Map build: ${widget.control.id} (${widget.control.hashCode})");
bool disabled = widget.control.isDisabled || widget.parentDisabled;

List<String> acceptedChildrenTypes = [
"map_circle_layer",
"map_tile_layer",
Expand All @@ -45,153 +60,87 @@ class _MapControlState extends State<MapControl> with FletStoreMixin {
.where((c) => c.isVisible && (acceptedChildrenTypes.contains(c.type)))
.toList();

void triggerEvent(Control ctrl, String eventName, dynamic eventData) {
var d = "";
if (eventData is String) {
d = eventData;
} else if (eventData is Map) {
d = json.encode(eventData);
}
Curve? defaultAnimationCurve;
Duration? defaultAnimationDuration;
var configuration = parseConfiguration(widget.control, "configuration",
widget.backend, Theme.of(context), const MapOptions())!;

widget.backend.triggerControlEvent(ctrl.id, eventName, d);
}
Widget map = FlutterMap(
mapController: _animatedMapController.mapController,
options: configuration,
children: ctrls
.map((c) => createControl(widget.control, c.id, disabled))
.toList(),
);

return withControls(widget.control.childIds, (context, configurationsView) {
var configuration = configurationsView.controlViews
.where((c) => c.control.type == "map_configuration")
.map((config) {
var onTap = config.control.attrBool("onTap", false)!;
var onLongPress = config.control.attrBool("onLongPress", false)!;
var onSecondaryTap = config.control.attrBool("onSecondaryTap", false)!;
var onMapEvent = config.control.attrBool("onEvent", false)!;
var onInit = config.control.attrBool("onInit", false)!;
var onPointerDown = config.control.attrBool("onPointerDown", false)!;
var onPointerCancel =
config.control.attrBool("onPointerCancel", false)!;
var onPointerUp = config.control.attrBool("onPointerUp", false)!;
var onPositionChange =
config.control.attrBool("onPositionChange", false)!;

return MapOptions(
initialCenter: parseLatLng(
config.control, "initialCenter", const LatLng(50.5, 30.51))!,
backgroundColor: config.control
.attrColor("backgroundColor", context, const Color(0x00000000))!,
initialRotation: config.control.attrDouble("initialRotation", 0.0)!,
initialZoom: config.control.attrDouble("initialZoom", 13.0)!,
keepAlive: config.control.attrBool("keepAlive", false)!,
maxZoom: config.control.attrDouble("maxZoom"),
minZoom: config.control.attrDouble("minZoom"),
onTap: onTap
? (TapPosition pos, LatLng latlng) {
triggerEvent(config.control, "tap", {
"lat": latlng.latitude,
"long": latlng.longitude,
"gx": pos.global.dx,
"gy": pos.global.dy,
"lx": pos.relative?.dx,
"ly": pos.relative?.dy,
});
}
: null,
onLongPress: onLongPress
? (TapPosition pos, LatLng latlng) {
triggerEvent(config.control, "long_press", {
"lat": latlng.latitude,
"long": latlng.longitude,
"gx": pos.global.dx,
"gy": pos.global.dy,
"lx": pos.relative?.dx,
"ly": pos.relative?.dy,
});
}
: null,
onPositionChanged: onPositionChange
? (MapCamera camera, bool hasGesture) {
triggerEvent(config.control, "position_change", {
"lat": camera.center.latitude,
"long": camera.center.longitude,
"min_zoom": camera.minZoom,
"max_zoom": camera.maxZoom,
"rot": camera.rotation,
});
}
: null,
onPointerDown: onPointerDown
? (PointerDownEvent e, LatLng latlng) {
triggerEvent(config.control, "pointer_down", {
"lat": latlng.latitude,
"long": latlng.longitude,
"gx": e.position.dx,
"gy": e.position.dy,
"kind": e.kind.name,
});
}
: null,
onPointerCancel: onPointerCancel
? (PointerCancelEvent e, LatLng latlng) {
triggerEvent(config.control, "pointer_cancel", {
"lat": latlng.latitude,
"long": latlng.longitude,
"gx": e.position.dx,
"gy": e.position.dy,
"kind": e.kind.name,
});
}
: null,
onPointerUp: onPointerUp
? (PointerUpEvent e, LatLng latlng) {
triggerEvent(config.control, "pointer_up", {
"lat": latlng.latitude,
"long": latlng.longitude,
"gx": e.position.dx,
"gy": e.position.dy,
"kind": e.kind.name,
});
}
: null,
onSecondaryTap: onSecondaryTap
? (TapPosition pos, LatLng latlng) {
triggerEvent(config.control, "secondary_tap", {
"lat": latlng.latitude,
"long": latlng.longitude,
"gx": pos.global.dx,
"gy": pos.global.dy,
"lx": pos.relative?.dx,
"ly": pos.relative?.dy,
});
}
: null,
onMapEvent: onMapEvent
? (MapEvent e) {
triggerEvent(config.control, "event", {
"src": e.source.name,
"c_lat": e.camera.center.latitude,
"c_long": e.camera.center.longitude,
"zoom": e.camera.zoom,
"min_zoom": e.camera.minZoom,
"max_zoom": e.camera.maxZoom,
"rot": e.camera.rotation,
});
}
: null,
onMapReady: onInit
? () {
debugPrint("Map ${widget.control.id} init");
widget.backend.triggerControlEvent(config.control.id, "init");
}
: null,
);
() async {
widget.backend.subscribeMethods(widget.control.id,
(methodName, args) async {
switch (methodName) {
case "rotate_from":
var degree = parseDouble(args["degree"]);
if (degree != null) {
_animatedMapController.animatedRotateFrom(
degree,
curve: parseCurve(args["curve"]) ?? defaultAnimationCurve,
);
}
case "reset_rotation":
_animatedMapController.animatedRotateReset(
curve: parseCurve(args["curve"], defaultAnimationCurve),
duration: durationFromString(
args["duration"], defaultAnimationDuration));
case "zoom_in":
_animatedMapController.animatedZoomIn(
curve: parseCurve(args["curve"], defaultAnimationCurve),
duration: durationFromString(
args["duration"], defaultAnimationDuration));
case "zoom_out":
_animatedMapController.animatedZoomOut(
curve: parseCurve(args["curve"], defaultAnimationCurve),
duration: durationFromString(
args["duration"], defaultAnimationDuration));
case "zoom_to":
var zoom = parseDouble(args["zoom"]);
if (zoom != null) {
_animatedMapController.animatedZoomTo(zoom,
curve: parseCurve(args["curve"], defaultAnimationCurve),
duration: durationFromString(
args["duration"], defaultAnimationDuration));
}
case "move_to":
var zoom = parseDouble(args["zoom"]);
var lat = parseDouble(args["lat"]);
var long = parseDouble(args["long"]);
var ox = parseDouble(args["ox"]);
var oy = parseDouble(args["oy"]);
_animatedMapController.animateTo(
zoom: zoom,
curve: parseCurve(args["curve"], defaultAnimationCurve),
rotation: parseDouble(args["rot"]),
duration: durationFromString(
args["duration"], defaultAnimationDuration),
dest: (lat != null && long != null) ? LatLng(lat, long) : null,
offset: (ox != null && oy != null) ? Offset(ox, oy) : Offset.zero,
);
case "center_on":
var zoom = parseDouble(args["zoom"]);
var lat = parseDouble(args["lat"]);
var long = parseDouble(args["long"]);
if (lat != null && long != null) {
_animatedMapController.centerOnPoint(
LatLng(lat, long),
zoom: zoom,
curve: parseCurve(args["curve"], defaultAnimationCurve),
duration: durationFromString(
args["duration"], defaultAnimationDuration),
);
}
}
return null;
});
}();

Widget map = FlutterMap(
options: configuration.first,
children: ctrls
.map((c) => createControl(widget.control, c.id, disabled))
.toList(),
);

return constrainedControl(context, map, widget.parent, widget.control);
});
return constrainedControl(context, map, widget.parent, widget.control);
}
}
Loading