Creating Custom Markers in Flutter: A Comprehensive Guide (Widget to custom marker).

Pvaddoriya
3 min readSep 4, 2023

--

Introduction

Flutter is a versatile framework for building natively compiled applications for mobile, web, and desktop from a single codebase. When it comes to building location-based applications, such as maps, custom markers can enhance the user experience significantly. In this guide, we will explore how to generate custom markers in Flutter, giving you the ability to personalize your map-based applications.

Why Custom Markers?

Custom markers allow you to replace the default map pins with icons, images, or any other widget you desire. Whether you want to show specific landmarks, highlight important locations, or just add a touch of personalization to your map, custom markers can make your Flutter app stand out.

Getting Started

To create custom markers in Flutter, you’ll need to use the google_maps_flutter package. Start by adding this package to your pubspec.yaml

dependencies:
flutter:
sdk: flutter
google_maps_flutter: ^2.0.10

Run flutter pub get to fetch the package.

Creating Custom Marker:

Create one file named widget_to_map_icon.dart

copy below content to that file

import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

extension ToBitDescription on Widget {
Future<BitmapDescriptor> toBitmapDescriptor({Size? logicalSize, Size? imageSize, Duration waitToRender = const Duration(milliseconds: 300), TextDirection textDirection = TextDirection.ltr}) async {
final widget = RepaintBoundary(
child: MediaQuery(data: const MediaQueryData(), child: Directionality(textDirection: TextDirection.ltr, child: this)),
);
final pngBytes = await createImageFromWidget(widget, waitToRender: waitToRender, logicalSize: logicalSize, imageSize: imageSize);
return BitmapDescriptor.fromBytes(pngBytes);
}
}

/// Creates an image from the given widget by first spinning up a element and render tree,
/// wait [waitToRender] to render the widget that take time like network and asset images

/// The final image will be of size [imageSize] and the the widget will be layout, ... with the given [logicalSize].
/// By default Value of [imageSize] and [logicalSize] will be calculate from the app main window

Future<Uint8List> createImageFromWidget(Widget widget, {Size? logicalSize, required Duration waitToRender, Size? imageSize}) async {
final RenderRepaintBoundary repaintBoundary = RenderRepaintBoundary();
final view = ui.PlatformDispatcher.instance.views.first;
logicalSize ??= view.physicalSize / view.devicePixelRatio;
imageSize ??= view.physicalSize;

// assert(logicalSize.aspectRatio == imageSize.aspectRatio);

final RenderView renderView = RenderView(
view: view,
child: RenderPositionedBox(alignment: Alignment.center, child: repaintBoundary),
configuration: ViewConfiguration(
size: logicalSize,
devicePixelRatio: 1.0,
),
);

final PipelineOwner pipelineOwner = PipelineOwner();
final BuildOwner buildOwner = BuildOwner(focusManager: FocusManager());

pipelineOwner.rootNode = renderView;
renderView.prepareInitialFrame();

final RenderObjectToWidgetElement<RenderBox> rootElement = RenderObjectToWidgetAdapter<RenderBox>(
container: repaintBoundary,
child: widget,
).attachToRenderTree(buildOwner);

buildOwner.buildScope(rootElement);

await Future.delayed(waitToRender);

buildOwner.buildScope(rootElement);
buildOwner.finalizeTree();

pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
pipelineOwner.flushPaint();

final ui.Image image = await repaintBoundary.toImage(pixelRatio: imageSize.width / logicalSize.width);
final ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);

return byteData!.buffer.asUint8List();
}

Google map always need marker type as a BitmapDescriptor.

So convert widget in to BitmapDescriptor. just add extention to that marker like below.

Future<BitmapDescriptor> getCustomIcon() async {
return SizedBox(
height: 200,
width: 200,
child: Image.asset("temp image"),
).toBitmapDescriptor();
}

Make an marker list like below

  List<Marker> markerList = <Marker>[];

markerList.add(
Marker(
markerId: const MarkerId("MarkerId"),
position: LatLng(21.000000, 72.000000),
icon: await getCustomIcon(),
),
)

Now finally, Use this marker list as below to show custom marker in google map.

GoogleMap(
mapType: MapType.hybrid,
arkers: markerList.toSet()
initialCameraPosition: _kGooglePlex,
onMapCreated: (GoogleMapController controller) {
_controller.complete(controller);
},
)

Output will look like :-

Conclusion

In this guide, we’ve walked through the process of creating custom markers in Flutter for Google Maps. By following these steps, you can enhance your map-based applications with personalized markers that cater to your specific needs.

Custom markers are a powerful tool in Flutter, allowing you to create visually appealing and informative maps for your users. Experiment with different marker designs and see how they can elevate your Flutter applications to the next level. Happy coding!

--

--