I try to create a custom marker (BitmapDescriptor
) in Flutter to display it on a screen where I use Google Maps library.
The only problem I have is that my custom marker is positioned on the wrong place on the map. If I use a default marker instead then the position of the marker is displayed correct.
Below is my code and also 2 screenshots with the right position of the marker and also the wrong position of the marker (custom marker).
Screenshot Marker – Right Position
Screenshot Marker – Wrong Position
/// Load an asset from root library and return it back as a list of bytes
Future<Uint8List> loadImageAsBytes( {required String path, required Size size}) async {
ByteData data = await rootBundle.load(path);
ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),
targetWidth: size.width.toInt());
ui.FrameInfo fi = await codec.getNextFrame();
return ((await fi.image.toByteData(format: ui.ImageByteFormat.png)) ??
ByteData(0))
.buffer
.asUint8List();
}
/// Load the icon from a list of bytes and return it back as an Image
Future<ui.Image> getImageFromPath(String imagePath, Size iconSize) async {
Uint8List imageBytes = await loadImageAsBytes(size: iconSize, path: imagePath);
final Completer<ui.Image> completer = Completer();
ui.decodeImageFromList(imageBytes, (ui.Image img) {
return completer.complete(img);
});
return completer.future;
}
/// Here we draw a custom marker which will include the vehicle icon and vehicle plate number.
Future<BitmapDescriptor> createCustomMarkerWithPlateRegAndIcon({required String iconPath, required Size size, required String plateReg}) async {
// Create a TextBox where to place the Vehicle Plate Number
TextSpan span = TextSpan(
style: const TextStyle(
height: 1.2,
color: Colors.black,
fontSize: 30.0,
fontWeight: FontWeight.bold,
),
text: plateReg);
// Align the Vehicle Plate Registration to center in the above TextBox
TextPainter tp = TextPainter(
text: span,
textAlign: TextAlign.center,
textDirection: TextDirection.ltr,
);
// Computes the visual position of the glyphs for painting the text.
tp.layout();
// Start recording a new drawing
ui.PictureRecorder recorder = ui.PictureRecorder();
// Create an empty canvas where the drawing will be painted
Canvas canvas = Canvas(recorder);
// TextBox background colour for Plate Registration (I set it to dark yellow to reflect UK style)
Paint textBgBoxPaint = Paint()
..color = const Color.fromARGB(255, 240, 200, 50);
// Create a rectangle where to place the Veh Plate Number
Rect rect = Rect.fromLTWH(0, 0, tp.width + 30, 50);
// Draw on the canvas the rectangle with yellow background and rounded corners
canvas.drawRRect(
RRect.fromRectAndRadius(rect, const Radius.circular(20.0)),
textBgBoxPaint,
);
// Add the Plate Registration to canvas and align it to center in the TextBox
tp.paint(canvas, const Offset(15.0, 5.0));
// Create a rectangle where will be placed the vehicle icon
Rect rectForImage = Rect.fromLTWH(
0,
55,
size.width,
size.height,
);
// Add path to rectangle image
canvas.clipPath(Path()..addRect(rectForImage));
// Get the vehicle icon which will gonna be inserted on the canvas
ui.Image image = await getImageFromPath(iconPath, size);
// Paint the icon on the canvas
paintImage(
canvas: canvas,
image: image,
rect: rectForImage,
fit: BoxFit.fitHeight);
// Stop the drawing
ui.Picture p = recorder.endRecording();
// Take the whole drawing and convert it to a PNG and after to a byte data
ByteData? pngBytes = await (await p.toImage(
160,
160,
))
.toByteData(format: ui.ImageByteFormat.png);
// This is an empty byte data for null safety
ByteData emptyData = Uint8List(0).buffer.asByteData();
// Convert the PNG from byteData to a list of bytes.
Uint8List data = Uint8List.view((pngBytes ?? emptyData).buffer);
// Return the PNG (in format list of bytes) as a Google Marker of type BitmapDescriptor
return BitmapDescriptor.fromBytes(data);
}
Thanks for reading this !
4
Answers
Solution 1:
Replace this with your code (add the same margin from the left):
Solution 2:
Please try this solution.
At first you need a class that will convert your marker image and then add text on top of the marker. You have to pass different marker size (like x1, x2, x3), you have to check your screen size and then pass the correct marker to the class below:
Then call this class from anywhere in your app and set it to your icon variable.
My returnImageUrl() and returnFontSize() functions just calculates my screen size and returns a pre defined image (like 1x, 2x) url from assets and fontSize. It helps my marker look symmetric for different screen sizes.
Then just pass your icon to Map marker:
I tried to make the marker as dynamic as possible. Play around with the MarkersWithLabel class to find your desired result. Check the screenshot for final result.
Code to resize the icon was added otherwise the icons was huge on the map like in the photo below:
The code was improved because in some cases the markers was cropped like in the photo below:
At the end the markers will look like this (for
iconSize
I’ve usedwidth
= 75 andheight
= 100).Photo below:
I have found an good solution to make custom widgets as Map Marker and wrote down an article , Check it out
https://medium.com/p/5e50d382bc99