Is it possible to use SVG paths to create a marker with the google_maps_flutter plugin? I know you can use .png
files by using:
icon: BitmapDescriptor.fromAsset("images/myFile.png")
How about SVG paths?
Thanks
Is it possible to use SVG paths to create a marker with the google_maps_flutter plugin? I know you can use .png
files by using:
icon: BitmapDescriptor.fromAsset("images/myFile.png")
How about SVG paths?
Thanks
This can be achieved using the flutter_svg package.
import 'dart:ui' as ui; // imported as ui to prevent conflict between ui.Image and the Image widget
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
Future<BitmapDescriptor> _bitmapDescriptorFromSvgAsset(BuildContext context, String assetName) async {
// Read SVG file as String
String svgString = await DefaultAssetBundle.of(context).loadString(assetName);
// Create DrawableRoot from SVG String
DrawableRoot svgDrawableRoot = await svg.fromSvgString(svgString, null);
// toPicture() and toImage() don't seem to be pixel ratio aware, so we calculate the actual sizes here
MediaQueryData queryData = MediaQuery.of(context);
double devicePixelRatio = queryData.devicePixelRatio;
double width = 32 * devicePixelRatio; // where 32 is your SVG's original width
double height = 32 * devicePixelRatio; // same thing
// Convert to ui.Picture
ui.Picture picture = svgDrawableRoot.toPicture(size: Size(width, height));
// Convert to ui.Image. toImage() takes width and height as parameters
// you need to find the best size to suit your needs and take into account the
// screen DPI
ui.Image image = await picture.toImage(width, height);
ByteData bytes = await image.toByteData(format: ui.ImageByteFormat.png);
return BitmapDescriptor.fromBytes(bytes.buffer.asUint8List());
}
You can then use the BitmapDescriptor to create a Marker as usual:
BitmapDescriptor bitmapDescriptor = await _bitmapDescriptorFromSvgAsset(context, 'assets/images/someimage.svg');
Marker marker = Marker(markerId: MarkerId('someId'), icon: bitmapDescriptor, position: LatLng(someLatitude, someLongitude));
With the latest version of flutter_svg (2.0.1 in my case) you can achieve it in that way:
class BitmapDescriptorHelper {
static Future<BitmapDescriptor> getBitmapDescriptorFromSvgAsset(
String assetName, [
Size size = const Size(48, 48),
]) async {
final pictureInfo = await vg.loadPicture(SvgAssetLoader(assetName), null);
double devicePixelRatio = ui.window.devicePixelRatio;
int width = (size.width * devicePixelRatio).toInt();
int height = (size.height * devicePixelRatio).toInt();
final scaleFactor = math.min(
width / pictureInfo.size.width,
height / pictureInfo.size.height,
);
final recorder = ui.PictureRecorder();
ui.Canvas(recorder)
..scale(scaleFactor)
..drawPicture(pictureInfo.picture);
final rasterPicture = recorder.endRecording();
final image = rasterPicture.toImageSync(width, height);
final bytes = (await image.toByteData(format: ui.ImageByteFormat.png))!;
return BitmapDescriptor.fromBytes(bytes.buffer.asUint8List());
}
}
ui.window
is deprecated you should use ui.PlatformDispatcher.instance.views.first.devicePixelRatio
. –
Auden Here are few options:
getBitmapDescriptorFromSvgAsset
- svg from assets getBitmapDescriptorFromSvgString
- svg as string from db/remote/whateverclass BitmapDescriptorHelper{
static Future<BitmapDescriptor> getBitmapDescriptorFromSvgAsset(
BuildContext context, String svgAssetLink) async {
final svgImage = await _getSvgImageFromAssets(context, svgAssetLink);
final sizedSvgImage = await _getSizedSvgImage(svgImage);
final pngSizedBytes = await sizedSvgImage.toByteData(format: ui.ImageByteFormat.png);
final unit8List = pngSizedBytes.buffer.asUint8List();
return BitmapDescriptor.fromBytes(unit8List);
}
static Future<BitmapDescriptor> getBitmapDescriptorFromSvgString(String svgString) async {
final svgImage = await _getSvgImageFromString(svgString);
final sizedSvgImage = await _getSizedSvgImage(svgImage);
final pngSizedBytes = await sizedSvgImage.toByteData(format: ui.ImageByteFormat.png);
final unit8List = pngSizedBytes.buffer.asUint8List();
return BitmapDescriptor.fromBytes(unit8List);
}
static Future<ui.Image> _getSvgImageFromAssets(BuildContext context, String svgAssertLink) async {
String svgString = await DefaultAssetBundle.of(context).loadString(svgAssertLink);
DrawableRoot drawableRoot = svg.fromSvgString(svgString, null);
ui.Picture picture = drawableRoot.toPicture();
ui.Image image = await picture.toImage(
drawableRoot.viewport.width.toInt(), drawableRoot.viewport.height.toInt());
return image;
}
static Future<ui.Image> _getSvgImageFromString(String svgString) async {
DrawableRoot drawableRoot = svg.fromSvgString(svgString, null);
ui.Picture picture = drawableRoot.toPicture();
ui.Image image = await picture.toImage(
drawableRoot.viewport.width.toInt(), drawableRoot.viewport.height.toInt());
return image;
}
static Future<ui.Image> _getSizedSvgImage(ui.Image svgImage) async {
final size = 50 * ui.window.devicePixelRatio;
final width = size;
final height = width;
final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
final Canvas canvas = Canvas(pictureRecorder);
final Rect svgRect =
Rect.fromLTRB(0.0, 0.0, svgImage.width.toDouble(), svgImage.height.toDouble());
final Rect sizedRect = Rect.fromLTRB(0.0, 0.0, width, height); // owr size here
canvas.drawImageRect(svgImage, svgRect, sizedRect, Paint());
return await pictureRecorder.endRecording().toImage(width.toInt(), height.toInt());
}
}
from your widget class:
final icon = await BitmapDescriptorHelper.getBitmapDescriptorFromSvgAsset(
context, "assets/icons/some_svg_icon.svg");
final Marker marker = Marker(
icon: icon,
...
);
enjoy :)
String busSvg() {
return '''<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<text x="40" y="40" font-size="40" text-anchor="middle" fill="black">🚌</text>
</svg>''';
}
Future<BitmapDescriptor> getSvgIcon() async {
String svgString = busSvg();
final PictureInfo pictureInfo =
await vg.loadPicture(SvgStringLoader(svgString), null);
final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
final Canvas canvas = Canvas(pictureRecorder);
canvas.drawPicture(pictureInfo.picture);
final ui.Image image = await pictureRecorder.endRecording().toImage(100, 100);
final ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
final Uint8List uint8list = byteData!.buffer.asUint8List();
return BitmapDescriptor.fromBytes(uint8list);
}
Marker(
icon: await getSvgIcon(),
markerId:...
)
Taking from all of the answer above this is what's working for me with the latest version of flutter_svg (2.0.10+1) :
1.Add the following import
import 'dart:ui' as ui;
import 'package:flutter/services.dart';
import 'package:flutter_svg flutter_svg.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
2.Add this svgToBitmap function to your code
Future<BitmapDescriptor> svgToBitmap(String asset) async {
final String svgString = await rootBundle.loadString(asset);
final fsvg.PictureInfo pictureInfo =
await fsvg.vg.loadPicture(fsvg.SvgStringLoader(svgString), null);
final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
final Canvas canvas = Canvas(pictureRecorder);
canvas.drawPicture(pictureInfo.picture);
final ui.Image image =
await pictureRecorder.endRecording().toImage(100, 100);
final ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
final Uint8List uint8list = byteData!.buffer.asUint8List();
return BitmapDescriptor.fromBytes(uint8list);
}
3.To use it just call and await the function in the GoogleMap Marker
Marker(
icon: await svgToBitmap(YourSVGAssetPath),
...
)
just checked out the docs for BitmapDescriptor class You can try the fromAssetImage() class method
© 2022 - 2024 — McMap. All rights reserved.