From 0aaa619eabdb3becd2a6492b7ef3e6544f589fd9 Mon Sep 17 00:00:00 2001 From: kundan Date: Thu, 29 Feb 2024 14:14:21 +0530 Subject: [PATCH 1/4] fix stream parameter issue --- lib/interactive_maps_marker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 08ec1e2..0391dc6 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -126,7 +126,7 @@ class InteractiveMapsMarkerState extends State { ) ], ); - }, + }, stream: null, ); } From 64161685f2a5da5e8fc17d6346309ca2ef9f149e Mon Sep 17 00:00:00 2001 From: kundan Date: Sat, 2 Mar 2024 15:11:03 +0530 Subject: [PATCH 2/4] custom marker added --- lib/interactive_maps_marker.dart | 147 ++++++++++++++++++++----------- 1 file changed, 95 insertions(+), 52 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 0391dc6..626a71d 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -1,6 +1,7 @@ library interactive_maps_marker; // interactive_marker_list import 'dart:async'; +import 'dart:ui'; import "package:flutter/material.dart"; import 'package:flutter/widgets.dart'; @@ -16,7 +17,8 @@ class MarkerItem { double latitude; double longitude; - MarkerItem({required this.id, required this.latitude, required this.longitude}); + MarkerItem( + {required this.id, required this.latitude, required this.longitude}); } class InteractiveMapsMarker extends StatefulWidget { @@ -25,6 +27,8 @@ class InteractiveMapsMarker extends StatefulWidget { final double zoom; final double zoomFocus; final bool zoomKeepOnTap; + void Function(CameraPosition)? onCameraMove; + void Function()? onCameraIdle; @required List items; @required @@ -50,16 +54,22 @@ class InteractiveMapsMarker extends StatefulWidget { this.contentAlignment = Alignment.bottomCenter, this.controller, this.onLastItem, - }){ - if(itemBuilder == null && itemContent == null){ + this.onCameraMove, + this.onCameraIdle, + }) { + if (itemBuilder == null && itemContent == null) { throw Exception('itemBuilder or itemContent must be provided'); } readIcons(); } void readIcons() async { - if (markerIcon == null) markerIcon = await getBytesFromAsset('packages/interactive_maps_marker/assets/marker.png', 100); - if (markerIconSelected == null) markerIconSelected = await getBytesFromAsset('packages/interactive_maps_marker/assets/marker_selected.png', 100); + if (markerIcon == null) + markerIcon = await getBytesFromAsset( + 'packages/interactive_maps_marker/assets/marker.png', 100); + if (markerIconSelected == null) + markerIconSelected = await getBytesFromAsset( + 'packages/interactive_maps_marker/assets/marker_selected.png', 100); } Uint8List? markerIcon; @@ -68,7 +78,7 @@ class InteractiveMapsMarker extends StatefulWidget { @override InteractiveMapsMarkerState createState() { var state = InteractiveMapsMarkerState(); - if(controller != null){ + if (controller != null) { controller!.currentState(state); } return state; @@ -83,7 +93,6 @@ class InteractiveMapsMarkerState extends State { Set markers = {}; int currentIndex = 0; ValueNotifier selectedMarker = ValueNotifier(0); - @override void initState() { rebuildMarkers(currentIndex); @@ -119,7 +128,9 @@ class InteractiveMapsMarkerState extends State { itemCount: widget.items.length, controller: pageController, onPageChanged: _pageChanged, - itemBuilder: widget.itemBuilder != null ? widget.itemBuilder! : _buildItem, + itemBuilder: widget.itemBuilder != null + ? widget.itemBuilder! + : _buildItem, ), ), ), @@ -142,6 +153,8 @@ class InteractiveMapsMarkerState extends State { myLocationEnabled: true, myLocationButtonEnabled: false, onMapCreated: _onMapCreated, + onCameraMove: widget.onCameraMove, + onCameraIdle: widget.onCameraIdle, initialCameraPosition: CameraPosition( target: widget.center, zoom: widget.zoom, @@ -178,65 +191,95 @@ class InteractiveMapsMarkerState extends State { void _pageChanged(int index) { try { setState(() => currentIndex = index); - if(widget.onLastItem != null && index == widget.items.length - 1){ + if (widget.onLastItem != null && index == widget.items.length - 1) { widget.onLastItem!(); } + + if (markers.isNotEmpty) { + Marker marker = markers.elementAt(index); + + mapController + ?.animateCamera( + widget.zoomKeepOnTap + ? CameraUpdate.newLatLng( + LatLng(marker.position.latitude, marker.position.longitude), + ) + : CameraUpdate.newCameraPosition( + CameraPosition(target: marker.position, zoom: widget.zoomFocus), + ), + ) + .then((val) { + setState(() {}); + }); + } + rebuildMarkers(index); - Marker marker = markers.elementAt(index); - - mapController - ?.animateCamera( - widget.zoomKeepOnTap - ? CameraUpdate.newLatLng( - LatLng(marker.position.latitude, marker.position.longitude), - ) - : CameraUpdate.newCameraPosition( - CameraPosition(target: marker.position, zoom: widget.zoomFocus), - ), - ) - .then((val) { - setState(() {}); - }); } catch (e) { print(e); } } + + Future rebuildMarkers(int index) async { - if(widget.items.length == 0) return; - int current = widget.items[index].id; - - Set _markers = Set(); - - widget.items.forEach((item) { - _markers.add( - Marker( - markerId: MarkerId(item.id.toString()), - position: LatLng(item.latitude, item.longitude), - onTap: () { - int tappedIndex = widget.items.indexWhere((element) => element.id == item.id); - pageController.animateToPage( - tappedIndex, - duration: Duration(milliseconds: 300), - curve: Curves.bounceInOut, - ); - _pageChanged(tappedIndex); - }, - icon: BitmapDescriptor.defaultMarkerWithHue(item.id == current ? BitmapDescriptor.hueGreen : BitmapDescriptor.hueRed), - // icon: item.id == current ? BitmapDescriptor.fromBytes(widget.markerIconSelected!) : BitmapDescriptor.fromBytes(widget.markerIcon!), - ), - ); - }); + if (widget.items.isEmpty) return; + + Set _markers = {}; + + for (var item in widget.items) { + Uint8List customIcon = await _createCustomMarker(item.id.toString(), item.id == widget.items[index].id); + + _markers.add(_createMarker(item, customIcon)); + } setState(() { markers = _markers; }); - // selectedMarker.value = current; - selectedMarker.value = current; - // selectedMarker.notifyListeners(); + selectedMarker.value = widget.items[index].id; + } + + Marker _createMarker(MarkerItem item, Uint8List customIcon) { + return Marker( + markerId: MarkerId(item.id.toString()), + position: LatLng(item.latitude, item.longitude), + onTap: () { + int tappedIndex = widget.items.indexWhere((element) => element.id == item.id); + pageController.animateToPage( + tappedIndex, + duration: Duration(milliseconds: 300), + curve: Curves.bounceInOut, + ); + _pageChanged(tappedIndex); + }, + icon: BitmapDescriptor.fromBytes(customIcon), + ); + } + + + Future _createCustomMarker(String id, bool isSelected) async { + final recorder = PictureRecorder(); + final canvas = Canvas(recorder); + final backgroundPaint = Paint()..color = isSelected ? Colors.green : Colors.red; + final textPaint = Paint()..color = Colors.white; + final textSpan = TextSpan(text: id, style: TextStyle(fontSize: 24, color: Colors.white)); + final textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr)..layout(); + + // Draw background circle + canvas.drawCircle(Offset(30, 30), 30, backgroundPaint); + + // Draw text + textPainter.paint(canvas, Offset(20, 20)); // Customize the text position + + final picture = recorder.endRecording(); + final image = await picture.toImage(60, 60); // Customize the image size + final ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); + if (byteData == null) { + throw Exception('Failed to convert image to byte data'); + } + return byteData.buffer.asUint8List(); } - void setIndex(int index){ + void setIndex(int index) { pageController.animateToPage( index, duration: Duration(milliseconds: 300), From a1f68dd7bf870ad8626a7803a21fddf9dd3090d1 Mon Sep 17 00:00:00 2001 From: kundan Date: Sun, 3 Mar 2024 11:12:21 +0530 Subject: [PATCH 3/4] custom marker update --- lib/interactive_maps_marker.dart | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 626a71d..7952573 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -252,26 +252,45 @@ class InteractiveMapsMarkerState extends State { _pageChanged(tappedIndex); }, icon: BitmapDescriptor.fromBytes(customIcon), + // icon: BitmapDescriptor.defaultMarkerWithHue(item.id == current ? BitmapDescriptor.hueGreen : BitmapDescriptor.hueRed), + ); } Future _createCustomMarker(String id, bool isSelected) async { + + final double radius = 40; + final double fontSize = 50; final recorder = PictureRecorder(); final canvas = Canvas(recorder); final backgroundPaint = Paint()..color = isSelected ? Colors.green : Colors.red; - final textPaint = Paint()..color = Colors.white; - final textSpan = TextSpan(text: id, style: TextStyle(fontSize: 24, color: Colors.white)); + final textSpan = TextSpan(text: id, style: TextStyle(fontSize: fontSize, color: Colors.white)); final textPainter = TextPainter(text: textSpan, textDirection: TextDirection.ltr)..layout(); + final borderPaint = Paint() + ..color = Colors.white // Border color + ..style = PaintingStyle.stroke + ..strokeWidth = 3.0; // Border width + + // Draw background circle - canvas.drawCircle(Offset(30, 30), 30, backgroundPaint); + canvas.drawCircle(Offset(radius, radius), radius, backgroundPaint); + + canvas.drawCircle(Offset(radius, radius), radius, borderPaint); + + + // Calculate text position + final textWidth = textPainter.width; + final textHeight = textPainter.height; + final textX = radius - (textWidth / 2); + final textY = radius - (textHeight / 2); // Draw text - textPainter.paint(canvas, Offset(20, 20)); // Customize the text position + textPainter.paint(canvas, Offset(textX, textY)); final picture = recorder.endRecording(); - final image = await picture.toImage(60, 60); // Customize the image size + final image = await picture.toImage((radius*2).toInt(), (radius*2).toInt()); // Customize the image size final ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); if (byteData == null) { throw Exception('Failed to convert image to byte data'); From 0e00a14d52ec40a7c496fefeb5937f1272b0c674 Mon Sep 17 00:00:00 2001 From: kundan Date: Wed, 13 Mar 2024 14:13:26 +0530 Subject: [PATCH 4/4] MarkerItem update --- lib/interactive_maps_marker.dart | 6 ++- pubspec.lock | 78 +++++++++++++++++++++----------- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/lib/interactive_maps_marker.dart b/lib/interactive_maps_marker.dart index 7952573..fa270b5 100644 --- a/lib/interactive_maps_marker.dart +++ b/lib/interactive_maps_marker.dart @@ -13,7 +13,7 @@ export 'package:interactive_maps_marker/interactive_maps_controller.dart'; import './utils.dart'; class MarkerItem { - int id; + String id; double latitude; double longitude; @@ -279,6 +279,8 @@ class InteractiveMapsMarkerState extends State { canvas.drawCircle(Offset(radius, radius), radius, borderPaint); + //make a small triangle in the bottom of circle also + // Calculate text position final textWidth = textPainter.width; @@ -290,6 +292,8 @@ class InteractiveMapsMarkerState extends State { textPainter.paint(canvas, Offset(textX, textY)); final picture = recorder.endRecording(); + + //change image width so triangle keep withing the canvas final image = await picture.toImage((radius*2).toInt(), (radius*2).toInt()); // Customize the image size final ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); if (byteData == null) { diff --git a/pubspec.lock b/pubspec.lock index 911f33b..013baf4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.18.0" fake_async: dependency: transitive description: @@ -99,46 +99,62 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.6" - js: + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: dependency: transitive description: - name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "2.0.1" matcher: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.11.0" path: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.9.0" plugin_platform_interface: dependency: transitive description: @@ -156,26 +172,26 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" stream_transform: dependency: transitive description: @@ -204,10 +220,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.6.1" vector_math: dependency: transitive description: @@ -216,6 +232,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=3.2.0-0 <4.0.0" flutter: ">=3.3.0"