Skip to content

Instantly share code, notes, and snippets.

@hungtrn75
Last active September 23, 2023 18:01
Show Gist options
  • Save hungtrn75/abe6fdff7c4f8c34859ff43c296c39f8 to your computer and use it in GitHub Desktop.
Save hungtrn75/abe6fdff7c4f8c34859ff43c296c39f8 to your computer and use it in GitHub Desktop.
@mapbox/mapbox-gl-draw in Flutter
import 'dart:math';
import 'package:collect_data/configs/constants/app_url.dart';
import 'package:flutter/material.dart';
import 'package:mapbox_gl/mapbox_gl.dart';
class Pair<T1, T2> {
final T1 first;
final T2 second;
Pair(this.first, this.second);
}
enum Annotation { fill, circle, middleCircle }
class MapboxGLDrawPage extends StatefulWidget {
const MapboxGLDrawPage({Key? key}) : super(key: key);
@override
State<MapboxGLDrawPage> createState() => _MapboxGLDrawPageState();
}
class _MapboxGLDrawPageState extends State<MapboxGLDrawPage> {
static const COLOR = "#ED7014";
late MapboxMapController _mapController;
List<LatLng> markers = [];
List<Circle> _circles = [];
List<Circle> _middleCircles = [];
Fill? _fill;
int? _active;
void _onMapCreated(MapboxMapController controller) {
_mapController = controller;
_mapController.onFeatureDrag.add(_onFeatureDrag);
_mapController.onCircleTapped.add(_onCircleTapped);
}
@override
void dispose() {
_mapController.onFeatureDrag.remove(_onFeatureDrag);
_mapController.onCircleTapped.remove(_onCircleTapped);
super.dispose();
}
Pair<Annotation, Circle?> isPrimaryCircle(dynamic id) {
final temp1 = _circles.where((element) => element.id == id);
if (temp1.isNotEmpty) {
return Pair(Annotation.circle, temp1.first);
}
final temp2 = _middleCircles.where((element) => element.id == id);
if (temp2.isNotEmpty) {
return Pair(Annotation.middleCircle, temp2.first);
}
return Pair(Annotation.fill, null);
}
void _onCircleTapped(Circle circle) {
final result = isPrimaryCircle(circle.id);
if (result.first == Annotation.circle) {
_active = _circles.indexOf(circle);
} else if (result.first == Annotation.middleCircle) {
final activeIndex = _middleCircles.indexOf(result.second!);
markers.insert(activeIndex + 1, result.second!.options.geometry!);
_active = activeIndex + 1;
}
_inject();
}
void _onFeatureDrag(id,
{required current,
required delta,
required origin,
required point,
required eventType}) {
DragEventType type = eventType;
final result = isPrimaryCircle(id);
switch (type) {
case DragEventType.start:
if (result.first == Annotation.middleCircle) {
_mapController.updateCircle(
result.second!,
CircleOptions(
circleRadius: 8,
circleStrokeWidth: 3,
circleColor: COLOR,
circleStrokeColor: "#FFFFFF",
geometry: current,
),
);
}
break;
case DragEventType.drag:
break;
case DragEventType.end:
if (_fill?.id == id) {
markers = _fill?.options.geometry?.first ?? [];
} else if (result.first == Annotation.circle) {
markers = _circles.map((e) => e.options.geometry!).toList();
} else {
final activeIndex = _middleCircles.indexOf(result.second!);
markers.insert(activeIndex + 1, result.second!.options.geometry!);
}
_inject();
break;
}
}
void _onStyleLoadedCallback() {}
void _onMapClickCallback(Point<double> point, LatLng coordinates) {
debugPrint('_onMapClickCallback');
markers.add(coordinates);
_inject();
}
void _onDelete() {
if (_active != null) {
markers.removeAt(_active!);
_active = null;
} else {
markers = [];
}
_inject();
}
void _inject() async {
await _mapController.clearCircles();
await _mapController.clearFills();
await _mapController.clearLines();
if (markers.length > 2) {
_middleCircles = [];
for (var i = 0; i < markers.length; i++) {
final current = markers[i];
final next = i + 1 < markers.length ? markers[i + 1] : markers.first;
final center = LatLng((current.latitude + next.latitude) / 2,
(current.longitude + next.longitude) / 2);
final temp = await _mapController.addCircle(
CircleOptions(
circleRadius: 5,
circleColor: COLOR,
geometry: center,
draggable: true),
);
_middleCircles.add(temp);
}
_fill = null;
_fill = await _mapController.addFill(
FillOptions(
fillColor: COLOR,
fillOpacity: 0.2,
geometry: [markers],
draggable: true,
),
);
}
if (markers.length > 1) {
await _mapController.addLine(LineOptions(
geometry: markers.length >2 ? [...markers, markers.first] : markers,
lineWidth: 2,
lineColor: COLOR,
lineOpacity: 0.4,
));
}
_circles = [];
for (var i = 0; i < markers.length; i++) {
final marker = markers[i];
final isActive = _active == i;
final temp = await _mapController.addCircle(CircleOptions(
circleRadius: isActive ? 8 : 6,
circleStrokeWidth: 3,
circleColor: COLOR,
circleStrokeColor: "#FFFFFF",
geometry: marker,
draggable: true,
));
_circles.add(temp);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: MapboxMap(
accessToken: AppUrl.MAP_ACCESS_TOKEN,
trackCameraPosition: true,
onMapCreated: _onMapCreated,
onMapClick: _onMapClickCallback,
onStyleLoadedCallback: _onStyleLoadedCallback,
initialCameraPosition: const CameraPosition(
target: LatLng(20.9756361, 105.7893619), zoom: 16),
annotationOrder: const [
AnnotationType.fill,
AnnotationType.line,
AnnotationType.symbol,
AnnotationType.circle,
],
annotationConsumeTapEvents: const [
AnnotationType.fill,
AnnotationType.line,
AnnotationType.symbol,
AnnotationType.circle,
],
),
floatingActionButton: Column(
children: [
FloatingActionButton(
onPressed: _onDelete,
child: const Icon(Icons.delete_outline),
),
],
mainAxisSize: MainAxisSize.min,
),
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment