flutterflutter-layoutflutter-widgetflutter-canvasfluttermap

How can I paint a Widget on a Canvas in Flutter?


Is there any way to paint a Widget at a given position on a Canvas?

More specifically, I want to paint the child widgets of Marker's related to a FlutterMap on a separate Canvas in front of the actual FlutterMap widget. Here's an attempt at creating a CustomPainter that would do that, but I can't figure out how to actually paint the widgets on the canvas. Using the RenderObject requires a PaintingContext, which I don't know how to create/retrieve:

class MarkerPainter extends CustomPainter {
  MapController mc;
  BuildContext context;
  List<Marker> markers;

  MarkerPainter(this.context, this.mc, this.markers);

  @override
  void paint(Canvas canvas, Size size) {
    if( markers != null && markers.isNotEmpty ){
      for(int i=0; i<markers.length; i++){
        Marker marker = markers[i];
        Offset o = myCalculateOffsetFromLatLng(marker.point, mc, context);

        // Won't work, this needs a PaintingContext...
        marker.builder(context).createElement().renderObject.paint(context, o); 
      }
    }
  }

  @override
  bool shouldRepaint(MarkerPainter oldDelegate) => oldDelegate.markers != markers;
}

Solution

  • You cannot do this with a CustomPainter.

    This class is only a simplification of the real deal: RenderObject, which has access to everything related to painter (and layout+more).

    What you should do is, instead of a CustomPainter, create a RenderBox (a 2d RenderObject)

    In your case, what you want is to draw a list of widgets. In that situation, you will need to create:

    Wrapping up, a widget used this way:

    MyExample(
      children: [
        Text('foo'),
        Text('bar'),
      ],
    ),
    

    would be written this way:

    import 'package:flutter/rendering.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/rendering.dart';
    
    class MyExample extends MultiChildRenderObjectWidget {
      MyExample({
        Key? key,
        required List<Widget> children,
      }) : super(key: key, children: children);
    
      @override
      RenderMyExample createRenderObject(BuildContext context) {
        return RenderMyExample();
      }
    }
    
    class MyExampleParentData extends ContainerBoxParentData<RenderBox> {}
    
    class RenderMyExample extends RenderBox
        with ContainerRenderObjectMixin<RenderBox, MyExampleParentData> {
      @override
      void setupParentData(RenderObject child) {
        if (child.parentData is! MyExampleParentData) {
          child.parentData = MyExampleParentData();
        }
      }
    
      @override
      void performLayout() {
        size = constraints.biggest;
    
        for (var child = firstChild; child != null; child = childAfter(child)) {
          child.layout(
            // limit children to a max height of 50
            constraints.copyWith(maxHeight: 50),
          );
        }
      }
    
      @override
      void paint(PaintingContext context, Offset offset) {
        // Paints all children in order vertically, separated by 50px
    
        var verticalOffset = .0;
        for (var child = firstChild; child != null; child = childAfter(child)) {
          context.paintChild(child, offset + Offset(0, verticalOffset));
    
          verticalOffset += 50;
        }
      }
    }