Comments (14)
One thing you could do, is take a look at my other plugin flutter_map_line_editor, as it seems to sort of what you want as a by product, and not have the issue you have (when it goes offscreen). It is using an older version of dragmarker tho, so that may have some effect. I don't have a lot of time to debug further into that atm tho, but it may help spot where the difference is.
from flutter_map_dragmarker.
And I'll take a look at the flutter_map_line_editor. Thanks a ton : )
Be aware that ibrierley/flutter_map_line_editor#36 might get merged, so that line editor reuses this package instead of its drag marker implementation and things can break for your use case.
from flutter_map_dragmarker.
Thanks for this! Good spot and surprised I missed this. I haven't got a lot of time for the next week, but I will try and sort this when I get chance.
from flutter_map_dragmarker.
This may fix it for version 4 (readying for flutter_map v3), so I'll try and get this type of solution added there where I get chance. If you need a quick hack, look at the stuff with marker.point and markerPoint which is now saved by initState and amend anything with those in it.
It may be a few days before I get time to test this a bit more and push to Git and publish to pub.dev.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
import 'package:flutter_map/plugin_api.dart';
class DragMarkers extends StatefulWidget {
final List<DragMarker> markers;
DragMarkers({Key? key, this.markers = const []});
@override
State<DragMarkers> createState() => _DragMarkersState();
}
class _DragMarkersState extends State<DragMarkers> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
var dragMarkers = <Widget>[];
FlutterMapState? mapState = FlutterMapState.maybeOf(context);
for (var marker in widget.markers) {
if (!_boundsContainsMarker(mapState, marker)) continue;
dragMarkers.add(DragMarkerWidget(
mapState: mapState,
marker: marker));
}
return Stack(children: dragMarkers);
}
static bool _boundsContainsMarker(FlutterMapState? map, DragMarker marker) {
var pixelPoint = map!.project(marker.point);
final width = marker.width - marker.anchor.left;
final height = marker.height - marker.anchor.top;
var sw = CustomPoint(pixelPoint.x + width, pixelPoint.y - height);
var ne = CustomPoint(pixelPoint.x - width, pixelPoint.y + height);
return map.pixelBounds.containsPartialBounds(Bounds(sw, ne));
}
}
class DragMarkerWidget extends StatefulWidget {
const DragMarkerWidget(
{Key? key,
this.mapState,
required this.marker,
AnchorPos? anchorPos})
//: anchor = Anchor.forPos(anchorPos, marker.width, marker.height);
: super(key: key);
final FlutterMapState? mapState;
//final Anchor anchor;
final DragMarker marker;
@override
State<DragMarkerWidget> createState() => _DragMarkerWidgetState();
}
class _DragMarkerWidgetState extends State<DragMarkerWidget> {
CustomPoint pixelPosition = const CustomPoint(0.0, 0.0);
late LatLng dragPosStart;
late LatLng markerPointStart;
late LatLng oldDragPosition;
bool isDragging = false;
late LatLng markerPoint;
static Timer? autoDragTimer;
@override
void initState() {
markerPoint = widget.marker.point;
super.initState();
}
@override
Widget build(BuildContext context) {
DragMarker marker = widget.marker;
updatePixelPos(markerPoint);
bool feedBackEnabled = isDragging && marker.feedbackBuilder != null;
Widget displayMarker = feedBackEnabled
? marker.feedbackBuilder!(context)
: marker.builder!(context);
return GestureDetector(
onPanStart: marker.useLongPress ? null : onPanStart,
onPanUpdate: marker.useLongPress ? null : onPanUpdate,
onPanEnd: marker.useLongPress ? null : onPanEnd,
onLongPressStart: marker.useLongPress ? onLongPanStart : null,
onLongPressMoveUpdate: marker.useLongPress ? onLongPanUpdate : null,
onLongPressEnd: marker.useLongPress ? onLongPanEnd : null,
onTap: () {
if (marker.onTap != null) {
marker.onTap!(markerPoint);
}
},
onLongPress: () {
if (marker.onLongPress != null) {
marker.onLongPress!(markerPoint);
}
},
child: Stack(children: [
Positioned(
width: marker.width,
height: marker.height,
left: pixelPosition.x +
((isDragging) ? marker.feedbackOffset.dx : marker.offset.dx),
top: pixelPosition.y +
((isDragging) ? marker.feedbackOffset.dy : marker.offset.dy),
child: widget.marker.rotateMarker
? Transform.rotate(
angle: -widget.mapState!.rotationRad, child: displayMarker)
: displayMarker)
]),
);
}
void updatePixelPos(point) {
DragMarker marker = widget.marker;
FlutterMapState? mapState = widget.mapState;
CustomPoint pos;
if (mapState != null) {
pos = mapState.project(point);
pos =
pos.multiplyBy(mapState.getZoomScale(mapState.zoom, mapState.zoom)) -
mapState.pixelOrigin;
pixelPosition = CustomPoint(
(pos.x - (marker.width - widget.marker.anchor.left)).toDouble(),
(pos.y - (marker.height - widget.marker.anchor.top)).toDouble());
}
}
void _start(Offset localPosition) {
isDragging = true;
dragPosStart = _offsetToCrs(localPosition);
markerPointStart =
LatLng(markerPoint.latitude, markerPoint.longitude);
}
void onPanStart(DragStartDetails details) {
_start(details.localPosition);
DragMarker marker = widget.marker;
if (marker.onDragStart != null) marker.onDragStart!(details, markerPoint);
}
void onLongPanStart(LongPressStartDetails details) {
_start(details.localPosition);
DragMarker marker = widget.marker;
if (marker.onLongDragStart != null) {
marker.onLongDragStart!(details, markerPoint);
}
}
void _pan(Offset localPosition) {
bool isDragging = true;
DragMarker marker = widget.marker;
FlutterMapState? mapState = widget.mapState;
var dragPos = _offsetToCrs(localPosition);
var deltaLat = dragPos.latitude - dragPosStart.latitude;
var deltaLon = dragPos.longitude - dragPosStart.longitude;
var pixelB = mapState?.getPixelBounds(mapState.zoom); //getLastPixelBounds();
var pixelPoint = mapState?.project(markerPoint);
/// If we're near an edge, move the map to compensate.
if (marker.updateMapNearEdge) {
/// How much we'll move the map by to compensate
var autoOffsetX = 0.0;
var autoOffsetY = 0.0;
if (pixelB != null && pixelPoint != null) {
if (pixelPoint.x + marker.width * marker.nearEdgeRatio >=
pixelB.topRight.x) autoOffsetX = marker.nearEdgeSpeed;
if (pixelPoint.x - marker.width * marker.nearEdgeRatio <=
pixelB.bottomLeft.x) autoOffsetX = -marker.nearEdgeSpeed;
if (pixelPoint.y - marker.height * marker.nearEdgeRatio <=
pixelB.topRight.y) autoOffsetY = -marker.nearEdgeSpeed;
if (pixelPoint.y + marker.height * marker.nearEdgeRatio >=
pixelB.bottomLeft.y) autoOffsetY = marker.nearEdgeSpeed;
}
/// Sometimes when dragging the onDragEnd doesn't fire, so just stops dead.
/// Here we allow a bit of time to keep dragging whilst user may move
/// around a bit to keep it going.
var lastTick = 0;
if (autoDragTimer != null) lastTick = autoDragTimer!.tick;
if ((autoOffsetY != 0.0) || (autoOffsetX != 0.0)) {
adjustMapToMarker(widget, autoOffsetX, autoOffsetY);
if ((autoDragTimer == null || autoDragTimer?.isActive == false) &&
(isDragging == true)) {
autoDragTimer =
Timer.periodic(const Duration(milliseconds: 10), (Timer t) {
var tick = autoDragTimer?.tick;
bool tickCheck = false;
if (tick != null) {
if (tick > lastTick + 15) {
tickCheck = true;
}
}
if (isDragging == false || tickCheck) {
autoDragTimer?.cancel();
} else {
/// Note, we may have adjusted a few lines up in same drag,
/// so could test for whether we've just done that
/// this, but in reality it seems to work ok as is.
adjustMapToMarker(widget, autoOffsetX, autoOffsetY);
}
});
}
}
}
setState(() {
markerPoint = LatLng(markerPointStart.latitude + deltaLat,
markerPointStart.longitude + deltaLon);
updatePixelPos(markerPoint);
});
}
void onPanUpdate(DragUpdateDetails details) {
_pan(details.localPosition);
DragMarker marker = widget.marker;
if (marker.onDragUpdate != null) {
marker.onDragUpdate!(details, markerPoint);
}
}
void onLongPanUpdate(LongPressMoveUpdateDetails details) {
_pan(details.localPosition);
DragMarker marker = widget.marker;
if (marker.onLongDragUpdate != null) {
marker.onLongDragUpdate!(details, markerPoint);
}
}
/// If dragging near edge of the screen, adjust the map so we keep dragging
void adjustMapToMarker(DragMarkerWidget widget, autoOffsetX, autoOffsetY) {
DragMarker marker = widget.marker;
FlutterMapState? mapState = widget.mapState;
var oldMapPos = mapState?.project(mapState.center);
LatLng? newMapLatLng;
CustomPoint<num>? oldMarkerPoint;
if (oldMapPos != null) {
newMapLatLng = mapState?.unproject(
CustomPoint(oldMapPos.x + autoOffsetX, oldMapPos.y + autoOffsetY));
oldMarkerPoint = mapState?.project(markerPoint);
}
if (mapState != null && newMapLatLng != null && oldMarkerPoint != null) {
markerPoint = mapState.unproject(CustomPoint(
oldMarkerPoint.x + autoOffsetX, oldMarkerPoint.y + autoOffsetY));
mapState.move(newMapLatLng, mapState.zoom, source: MapEventSource.onDrag);
}
}
void _end() {
isDragging = false;
if (autoDragTimer != null) autoDragTimer?.cancel();
}
void onPanEnd(details) {
_end();
if (widget.marker.onDragEnd != null) {
widget.marker.onDragEnd!(details, markerPoint);
}
setState(() {}); // Needed if using a feedback widget
}
void onLongPanEnd(details) {
_end();
if (widget.marker.onLongDragEnd != null) {
widget.marker.onLongDragEnd!(details, markerPoint);
}
setState(() {}); // Needed if using a feedback widget
}
static CustomPoint _offsetToPoint(Offset offset) {
return CustomPoint(offset.dx, offset.dy);
}
LatLng _offsetToCrs(Offset offset) {
// Get the widget's offset
var renderObject = context.findRenderObject() as RenderBox;
var width = renderObject.size.width;
var height = renderObject.size.height;
var mapState = widget.mapState;
// convert the point to global coordinates
var localPoint = _offsetToPoint(offset);
var localPointCenterDistance =
CustomPoint((width / 2) - localPoint.x, (height / 2) - localPoint.y);
if (mapState != null) {
var mapCenter = mapState.project(mapState.center);
var point = mapCenter - localPointCenterDistance;
return mapState.unproject(point);
}
return LatLng(0, 0);
}
}
class DragMarker {
LatLng point;
final WidgetBuilder? builder;
final WidgetBuilder? feedbackBuilder;
final double width;
final double height;
final Offset offset;
final Offset feedbackOffset;
final bool useLongPress;
final Function(DragStartDetails, LatLng)? onDragStart;
final Function(DragUpdateDetails, LatLng)? onDragUpdate;
final Function(DragEndDetails, LatLng)? onDragEnd;
final Function(LongPressStartDetails, LatLng)? onLongDragStart;
final Function(LongPressMoveUpdateDetails, LatLng)? onLongDragUpdate;
final Function(LongPressEndDetails, LatLng)? onLongDragEnd;
final Function(LatLng)? onTap;
final Function(LatLng)? onLongPress;
final bool updateMapNearEdge;
final double nearEdgeRatio;
final double nearEdgeSpeed;
final bool rotateMarker;
late Anchor anchor;
DragMarker({
required this.point,
this.builder,
this.feedbackBuilder,
this.width = 30.0,
this.height = 30.0,
this.offset = const Offset(0.0, 0.0),
this.feedbackOffset = const Offset(0.0, 0.0),
this.useLongPress = false,
this.onDragStart,
this.onDragUpdate,
this.onDragEnd,
this.onLongDragStart,
this.onLongDragUpdate,
this.onLongDragEnd,
this.onTap,
this.onLongPress,
this.updateMapNearEdge = false, // experimental
this.nearEdgeRatio = 1.5,
this.nearEdgeSpeed = 1.0,
this.rotateMarker = true,
AnchorPos? anchorPos,
}) {
anchor = Anchor.forPos(anchorPos, width, height);
}
}
from flutter_map_dragmarker.
Thanks for the quick response @ibrierley
from flutter_map_dragmarker.
@SheenaJacob can you confirm if the problem is solved to close this issue?
from flutter_map_dragmarker.
So the first part of the issue is now fixed, which means that a rebuild no longer triggers a change in the position of a marker while dragging.
I'm still facing an issue when dragging a marker whose position is constantly updated on onDragUpdate() outside the border though. At some point, the marker gets stuck when moving it outside the border even though the drag action is still taking place.
For example, in the video below there are two markers. In the first part of the video the black marker is dragged outside the border and I can bring it back without facing any problems. On the other hand, when dragging the red marker outside, the drag event can no longer be recognized and at 0:05 seconds you can see that the position displayed at the bottom is no longer updated even though I'm still moving the mouse. The difference between the two markers is that the black marker's position is not updated, while the red marker's position is updated every time the onDragUpdate is called.
Screen.Recording.2023-05-19.at.08.19.26.mov
The code for the example above is :
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_map/plugin_api.dart';
import 'package:flutter_map_dragmarker/dragmarker.dart';
import 'package:latlong2/latlong.dart';
class DragMarkerBoundaryTest extends StatefulWidget {
const DragMarkerBoundaryTest({super.key});
@override
State<DragMarkerBoundaryTest> createState() => _DragMarkerBoundaryTestState();
}
class _DragMarkerBoundaryTestState extends State<DragMarkerBoundaryTest> {
final LatLng _marker1Position = LatLng(44.1461, 9.9000);
LatLng _marker2Position = LatLng(44.1461, 10.1122);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
floatingActionButton: Text(
' Marker1 Position : $_marker1Position \n Marker2 Position : $_marker2Position'),
body: Center(
child: FlutterMap(
options: MapOptions(
absorbPanEventsOnScrollables: false,
center: _marker1Position,
zoom: 10.4,
maxZoom: 18.0),
children: [
TileLayer(
urlTemplate:
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: const ['a', 'b', 'c']),
DragMarkers(
markers: [
DragMarker(
point: _marker1Position,
width: 80.0,
height: 80.0,
offset: const Offset(0.0, -8.0),
builder: (ctx) => const Icon(Icons.location_on,
size: 50, color: Colors.black),
feedbackBuilder: (ctx) => const Icon(Icons.edit_location,
size: 50, color: Colors.black),
feedbackOffset: const Offset(0.0, -8.0),
),
DragMarker(
point: _marker2Position,
width: 80.0,
height: 80.0,
offset: const Offset(0.0, -8.0),
builder: (ctx) => const Icon(Icons.location_on,
size: 50, color: Colors.red),
feedbackBuilder: (ctx) => const Icon(Icons.edit_location,
size: 50, color: Colors.red),
feedbackOffset: const Offset(0.0, -8.0),
onDragUpdate: (details, point) {
setState(() {
_marker2Position = point;
});
},
)
],
)
],
),
),
),
);
}
}
Platform Tested on : macOS
flutter_map: ^3.1.0
flutter_map_dragmarker: ^4.1.2
I'm not sure if this is the intended behavior or if it is not recommended to update the position onDragUpdate, but in my use case, I need the position of the marker when it's being dragged.
from flutter_map_dragmarker.
Hmm I don't think that's a good idea in general (currently), it's not currently intended for that to be updated iirc. However, what's the use case, because it provides the location of the marker, so not quite sure why you need to update it whilst it's mid drag ?
from flutter_map_dragmarker.
Ok. That makes sense. My current use case is to find the distance between two markers, and visually it would look something like this. So I need to update the position of the markers so that I can draw a line between the two points.
![Screenshot 2023-05-19 at 10 04 26](https://private-user-images.githubusercontent.com/60810797/239476510-8ef644ad-1015-476e-b80a-fc931e245a8f.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MDc5ODY0NjYsIm5iZiI6MTcwNzk4NjE2NiwicGF0aCI6Ii82MDgxMDc5Ny8yMzk0NzY1MTAtOGVmNjQ0YWQtMTAxNS00NzZlLWI4MGEtZmM5MzFlMjQ1YThmLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDAyMTUlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwMjE1VDA4MzYwNlomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWNlMDM1ZjAwNmYyZDc4Yjc5NGM4ODBjM2Y2NmZjOWIzMDk2ZTVmZjQyMTRhNmI2ZGFmOTQzZWUxNmQxNjMxNDgmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.gpIFq9MmJ-SjChtasUVNonYdfDw74hZg2MPada3w9ME)
from flutter_map_dragmarker.
Ok, you could try updating the point, but not calling setState on the map out of interest, see what happens. I think the problem is that when setState is called, the gesture handlers lose their dragging (That would need a bit longer to test all of that to check if I'm going mad or not :))
from flutter_map_dragmarker.
Actually, scrap that I think, as you would probably need setState to update the lines...
from flutter_map_dragmarker.
Hehe. Yea. So I can use a workaround where I just have two variables that define the same point like this:
LatLng _markerPosition = LatLng(44.1461, 9.9000);
final LatLng _initialMarkerPosition = LatLng(44.1461, 9.9000);
DragMarker(
point: _initialMarkerPosition,
width: 80.0,
height: 80.0,
offset: const Offset(0.0, -8.0),
builder: (ctx) => const Icon(Icons.location_on,
size: 50, color: Colors.black),
feedbackBuilder: (ctx) => const Icon(Icons.edit_location,
size: 50, color: Colors.black),
feedbackOffset: const Offset(0.0, -8.0),
onDragUpdate: (details, point) {
setState(() {
_markerPosition = point;
});
},
),
but I don't understand why updating the value causes the gesture callbacks to stop working and also it only happens when it's dragged outside.
from flutter_map_dragmarker.
And I'll take a look at the flutter_map_line_editor. Thanks a ton : )
from flutter_map_dragmarker.
Yep, I wasn't really meaning to use that, just to try and spot why that doesn't break the dragging off screen when calling setState in comparison.
from flutter_map_dragmarker.
Related Issues (20)
- Dependency conflict with flutter_map_tappable_polyline HOT 2
- Rotable markers - The original flutter_map has rotab HOT 1
- Rotatable markers? HOT 9
- Flutter_Map v2.0.0 - Incompatible HOT 3
- Flutter_Map no longer appears to have required parameters HOT 2
- Flutter_Map v3.0.0 - Incompatible HOT 3
- Programmatically update the location of a marker that already exists HOT 4
- DragMarkers randomly disappear when dragging flutter_map HOT 7
- Issue while dragging marker HOT 4
- Change color or icon after creation HOT 20
- Draggable Markers don't register drag or tap events sometimes after zooming HOT 15
- Not compatible with flutter_map v4.0.0 HOT 10
- Not Compatible With latlong2 v0.9.0 HOT 6
- Not compatible with flutter_map v5.0.0 HOT 2
- Readme example is outdated HOT 1
- Not compatible with flutter_map v6.0.0 HOT 6
- Is there way to combine flutter_map_marker_popup and flutter_map_dragmarker? HOT 5
- Dragging problem HOT 2
- Marker display position is misaligned and offset does not work. HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from flutter_map_dragmarker.