GoogleMaps flutter rendering memory images with different size of the original

17 Views Asked by At

I'm currently implementing a way to render my widgets that represents a Marker so I can then get an image of that widget using RepaintBoundary. It worked well, but then when I tried to use it inside the package google_maps_flutter, it seems like the package is shrinking my image by default.

The following picture contain my original widget and an instance of the GoogleMaps with a marker containing an image that was loaded using the BitmapDescriptor.fromBytes constructor. If I use something like Image.memory, the image renders correctly (the second image).

Image using the GoogleMaps

Image using the GoogleMaps

Image using Image.memory

Image using Image.memory

Code

MarkerGenerator (The code that I found. It will render a List of widgets and then return a equivalent List of Uint8List).

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

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

class MarkerGenerator {
  final Function(List<Uint8List>) callback;
  final List<Widget> markerWidgets;

  MarkerGenerator(this.markerWidgets, this.callback);

  void generate(BuildContext context) {
    WidgetsBinding.instance
        .addPostFrameCallback((_) => afterFirstLayout(context));
  }

  void afterFirstLayout(BuildContext context) {
    addOverlay(context);
  }

  void addOverlay(BuildContext context) {
    final overlayState = Overlay.of(context);

    late OverlayEntry entry;

    entry = OverlayEntry(
      builder: (context) {
        return _MarkerHelper(
          markerWidgets: markerWidgets,
          callback: (List<Uint8List> bitmapList) {
            callback.call(bitmapList);

            entry.remove();
          },
        );
      },
      maintainState: true,
    );

    overlayState.insert(entry);
  }
}

class _MarkerHelper extends StatefulWidget {
  final List<Widget> markerWidgets;
  final Function(List<Uint8List>) callback;

  const _MarkerHelper({
    required this.markerWidgets,
    required this.callback,
  });

  @override
  _MarkerHelperState createState() => _MarkerHelperState();
}

class _MarkerHelperState extends State<_MarkerHelper> with AfterLayoutMixin {
  List<GlobalKey> globalKeys = [];

  @override
  void afterFirstLayout(BuildContext context) {
    _getBitmaps(context).then((list) {
      widget.callback(list);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Transform.translate(
      offset: Offset(MediaQuery.of(context).size.width, 0),
      child: Material(
        type: MaterialType.transparency,
        child: Stack(
          children: widget.markerWidgets.map((i) {
            final markerKey = GlobalKey();

            globalKeys.add(markerKey);

            return RepaintBoundary(
              key: markerKey,
              child: i,
            );
          }).toList(),
        ),
      ),
    );
  }

  Future<List<Uint8List>> _getBitmaps(BuildContext context) async {
    final futures = globalKeys.map(_getUint8List);

    return Future.wait(futures);
  }

  Future<Uint8List> _getUint8List(GlobalKey markerKey) async {
    final boundary =
        markerKey.currentContext?.findRenderObject() as RenderRepaintBoundary?;

    if (boundary == null) {
      return Uint8List(0);
    }

    final image = await boundary.toImage();

    final byteData = await image.toByteData(format: ui.ImageByteFormat.png);

    return byteData?.buffer.asUint8List() ?? Uint8List(0);
  }
}

mixin AfterLayoutMixin<T extends StatefulWidget> on State<T> {
  @override
  void initState() {
    super.initState();

    WidgetsBinding.instance
        .addPostFrameCallback((_) => afterFirstLayout(context));
  }

  void afterFirstLayout(BuildContext context);
}

Then a method that will be something like this to transform the Uint8List to a list of Marker

List<Marker> mapBitmapsToMarkers(
    List<Uint8List> bitmaps,
    List<HMapPin> pins,
  ) {
    final markersList = <Marker>[];

    bitmaps.asMap().forEach((i, bmp) {
      final pin = pins[i];

      markersList.add(
        Marker(
          markerId: MarkerId(pin.point.toString()),
          position: LatLng(pin.point.latitude, pin.point.longitude),
          icon: BitmapDescriptor.fromBytes(bmp),
        ),
      );
    });

    return markersList;
  }

Then is just a normal usage of the package.

Thanks in advance.

0

There are 0 best solutions below