In flutter is there a way to set a custom color for a google maps marker using a custom icon from a png

Viewed 1620

In flutter using google maps trying to place markers using a .png image with a custom color and size. I've got everything to work but the custom color, just can't figure out how to manipulate that without having multiple image assets all with different colors.

I'm using .png files stored as assets and using some codec and frameinfo to convert to bytes and manipulate the size. I have not been able to manipulate the color as well. I've seen examples of setting the base markers color but that doesn't let me specify the icon. I've seen examples of using canvas to build your own icon with shapes but that doesn't let me use a png as my icon (with the benefits of more formatting).

I'm trying to get both a custom icon and a custom color with a custom size.

Code looks kinda like this:

import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'dart:ui';

class ExplorePage extends StatefulWidget {
  @override
  ExplorePageState createState() => ExplorePageState();
}

class ExplorePageState extends State<ExplorePage> {
  final Map<String, Marker> markers = {};
  StreamSubscription placeSubscription;
  final LatLng center = const LatLng(37.6872, -97.3301);

  void updateLists(List<PlaceList> PlaceList) async {
    setState(() {
      listPlaceList = PlaceList;
    });
  }

  //called when the map is done being created
  void onMapCreated(GoogleMapController controller) {
    placeSubscription = placesBloc
        .getPlaces()
        //when the data changes update the map
        .listen(updateMarkers);
  }

  //gets called when the data or filter changes, updates the markers list that is tied
  //to the map and sets state so the map loads the markers
  void updateMarkers(List<Place> placeList) async {
    
final Uint8List plannedIcon =
        await getBytesFromAsset('assets/planned_marker.png', 100);
    final Uint8List doneIcon =
        await getBytesFromAsset('assets/done_marker.png', 100);
    final Uint8List blankIcon =
        await getBytesFromAsset('assets/blank_marker.png', 100);

    setState(() {
      placeList.forEach((Place place) {
        if (place.latlon != null) {
          final marker = Marker(
            markerId: MarkerId(place.docId),
            icon: place.done == null
                ? BitmapDescriptor.fromBytes(blankIcon)
                : place.done == 'Done!'
                    ? BitmapDescriptor.fromBytes(doneIcon)
                    : place.done == 'Planned'
                        ? BitmapDescriptor.fromBytes(plannedIcon)
                        : BitmapDescriptor.fromBytes(blankIcon),
            position: LatLng(place.latlon.latitude, place.latlon.longitude),
            infoWindow: InfoWindow(
              title: place.name,
              snippet: place.name,
            ),
          );
          markers[place.name] = marker;
        }
      });
    });
  }

  @override
  Widget build(BuildContext context) {
 
    return Scaffold(
        body: Stack(
      children: [
        GoogleMap(
          zoomGesturesEnabled: true,
          myLocationButtonEnabled: false,
          onMapCreated: onMapCreated,
          markers: markers.values.toSet(),
          initialCameraPosition: CameraPosition(
            target: center,
            zoom: 5.0,
          ),
        ),
        
      ],
    ));
  }

  Future<Uint8List> getBytesFromAsset(String path, int width) async {
    ByteData data = await rootBundle.load(path);
    Codec codec = await instantiateImageCodec(data.buffer.asUint8List(),
        targetWidth: width);
    FrameInfo fi = await codec.getNextFrame();
        
    return (await fi.image.toByteData(format: ImageByteFormat.png))
        .buffer
        .asUint8List();
  }
}
2 Answers

To custom Marker you will need use: BitmapDescriptor, we can create this object with contructor: BitmapDescriptor.fromBytes(). Your issue is how to create Uint8List from your png and dynamic color.

to resolve issue you can try a package support edit bitmap as image or create your canvas to draw image, edit and export to Uint8List. But I recommend the first way.

Took the approach suggested by dangngocduc using the linked image library. Here is how my syntax ended up using an asset image. The Offset ended up being the best setting to tweak color (I tried a bunch of them). Since it's anchored to the starting color on the image I converted by base photos to a black default color (otherwise I couldn't fully control the resulting color).

YAML Dependancy

  image: ^3.0.8

Import Statement

import 'package:image/image.dart' as img;

Code to start with an asset png and result in a BitmapDescriptor for use in a marker

ByteData donebytes = await rootBundle.load('assets/done_marker.png');
Uint8List doneU8 = donebytes.buffer
    .asUint8List(donebytes.offsetInBytes, donebytes.lengthInBytes);
List<int> doneListInt = doneU8.cast<int>();

img.Image doneImg = img.decodePng(doneListInt);

img.colorOffset(
  doneImg,
  red: 0,
  green: 0,
  blue: 250,
);


doneImg = img.copyResize(doneImg, width: 100);

final Uint8List doneIconColorful =
    Uint8List.fromList(img.encodePng(doneImg)); //doneImg.getBytes();
BitmapDescriptor doneBM = BitmapDescriptor.fromBytes(doneIconColorful);
Related