BoxyDelegate<LayoutData extends Object> class abstract

A delegate that controls the layout and paint of child widgets, used by CustomBoxy.

Delegates must ensure an identical delegate would produce the same layout. If your delegate takes arguments also make sure shouldRelayout and/or shouldRepaint return true when those fields change.

A single delegate can be used by multiple widgets at a time and should not keep any state. If you need to pass information from layout to another method, store it in layoutData or BoxyChild.parentData.

Delegates may access their children by name with getChild, alternatively they can be accessed through the children list.

The default constructor accepts Listenables that can trigger re-layout and re-paint. It's much more efficient for the boxy to listen directly to animations than rebuilding the CustomBoxy with a new delegate each frame.

Layout

Override layout to control the layout of children and return what size the boxy should be.

This method should call BoxyChild.layout for each child. It should also specify the position of each child with BoxyChild.position.

If the delegate does not depend on the size of a particular child, pass useSize: false to BoxyChild.layout, this prevents a change in the child's size from causing a re-layout.

The following example lays out two children like a column where the second widget is the same width as the first:

  @override
  Size layout() {
    // Get both children by a Symbol id
    var firstChild = getChild(#first);
    var secondChild = getChild(#second);

    // Lay out the first child with the incoming constraints
    var firstSize = firstChild.layout(constraints);
    firstChild.position(Offset.zero);

    // Lay out the second child
    var secondSize = secondChild.layout(
      constraints.deflate(
        // Subtract height consumed by the first child from the constraints
        EdgeInsets.only(top: firstSize.height),
      ).tighten(
        // Force width to be the same as the first child
        width: firstSize.width,
      )
    );

    // Position the second child below the first
    secondChild.position(Offset(0, firstSize.height));

    // Calculate the total size based both child sizes
    return Size(
      firstSize.width,
      firstSize.height + secondSize.height,
    );
  }

Painting

Override paint to draw behind children, this is similar to CustomPainter.paint which gives you a Canvas.

The following example simply gives the widget a blue background:

  @override
  void paint() {
    canvas.drawRect(
      Offset.zero & renderSize,
      Paint()..color = Colors.blue,
    );
  }

You can draw above children similarly by overriding paintForeground.

Override paintChildren to change how children themselves are painted, for example changing the order or adding layers.

The default behavior is to paint children at the BoxyChild.offset given to them during layout.

The canvas available to paintChildren is not transformed implicitly like paint and paintForeground, implementers of this method should draw at paintOffset and restore the canvas before painting a child. This is required by the framework because a child might need to insert its own compositing Layer between two other PictureLayers.

The following example draws a semi transparent rectangle between two children:

  @override
  void paintChildren() {
    getChild(#first).paint();
    canvas.drawRect(
      paintOffset & render.size,
      Paint()..color = Colors.blue.withOpacity(0.3),
    );
    getChild(#second).paint();
  }

Layers

In order to apply special effects to children such as transforms, opacity, clipping, etc. delegates will need to interact with the compositing tree. Boxy wraps this functionality conveniently with layers.

Before a delegate can push layers make sure to override needsCompositing. This getter can check the fields of the boxy to determine if compositing will be necessary, return true if that is the case.

The following example paints its child with 50% opacity:

  @override
  bool get needsCompositing => true;

  @override
  void paintChildren() {
    layers.opacity(
      opacity: 0.5,
      paint: () {
        getChild(#first).paint();
      },
    );
  }

Widget inflation

One of the most powerful features of the boxy library is to inflate arbitrary widgets at layout time, this would otherwise be extraordinarily difficult to implement in Flutter.

In layout delegates can inflate arbitrary widgets using the inflate method, this enables complex layouts where the contents of widgets change depending on the size and orientation of others, in addition to constraints.

After calling this method the child becomes available in children and any following painting and hit testing, it is removed from the list before the next layout.

Unlike children explicitly passed to CustomBoxy, Keys are not managed for widgets inflated during layout, this means a widgets state is preserved if inflated again with the same object id, rather than equal Keys.

The following example displays a text widget describing the size of another child:

  @override
  Size layout() {
    var firstChild = getChild(#first);

    var firstSize = firstChild.layout(constraints);
    firstChild.position(Offset.zero);

    var text = Padding(child: Text(
      "^ This guy is ${firstSize.width} x ${firstSize.height}",
      textAlign: TextAlign.center,
    ), padding: EdgeInsets.all(8));

    // Inflate the text widget
    var secondChild = inflate(text, id: #second);

    var secondSize = secondChild.layout(
      constraints.deflate(
        EdgeInsets.only(top: firstSize.height)
      ).tighten(
        width: firstSize.width
      )
    );

    secondChild.position(Offset(0, firstSize.height));

    return Size(
      firstSize.width,
      firstSize.height + secondSize.height,
    );
  }

See also:

Inheritance
Mixed in types

Constructors

BoxyDelegate({Listenable? relayout, Listenable? repaint})
Constructs a BoxyDelegate with optional relayout and repaint Listenables.

Properties

buildContext BuildContext
Gets the BuildContext of this boxy.
no setterinherited
canvas Canvas
The current canvas, should only be accessed from paint methods.
no setterinherited
children List<BoxyChild>
A list of each BoxyChild handle associated with the boxy, the list itself should not be modified by the delegate.
no setterinherited
constraints BoxConstraints
The most recent constraints given to this boxy by its parent.
no setterinherited
debugPhase BoxyDelegatePhase
The current phase in the render pipeline that this boxy is performing, only valid in debug mode.
no setterinherited
hashCode int
The hash code for this object.
no setterinherited
hitTestResult BoxHitTestResult
The current hit test result, should only be accessed from hitTest.
no setterinherited
indexedChildCount int
The number of children that have not been given a BoxyId, this guarantees there are child ids between 0 (inclusive) and indexedChildCount (exclusive).
no setterinherited
isDryLayout bool
Whether or not this boxy is performing a dry layout.
no setterinherited
layers BoxyLayerContext
The current layer context, useful for pushing Layers to the scene during paintChildren.
no setterinherited
layoutData ↔ LayoutData?
A variable to hold additional data created during layout which can be used while painting and hit testing.
getter/setter pairinherited
needsCompositing bool
Override this method to return true if the paint method will push one or more layers to paintingContext.
no setterinherited
paintingContext PaintingContext
The current painting context, should only be accessed from paint methods.
no setterinherited
paintOffset Offset
The offset of the current paint context.
no setterinherited
render RenderBoxy<BoxyChild>
The RenderBoxyMixin of the current context.
no setterinherited
renderSize Size
The last size returned by layout.
no setterinherited
runtimeType Type
A representation of the runtime type of the object.
no setterinherited

Methods

addHit() → void
Adds the boxy to hitTestResult, this should typically be called from hitTest when a hit succeeds.
inherited
distanceToBaseline(TextBaseline baseline) double?
Override to return the distance from the y-coordinate of the position of the box to the y-coordinate of the first given baseline in the box's contents, if any, or null otherwise.
inherited
getChild<T extends ChildHandleType>(Object id) → T
Gets the child handle with the specified id.
inherited
getChildOrNull<T extends ChildHandleType>(Object id) → T?
Gets the child handle with the specified id or returns null if there is no child with given id.
inherited
hasChild(Object id) bool
Returns true if a child exists with the specified id.
inherited
hitTest(SliverOffset position) bool
Override this method to change how the boxy gets hit tested.
inherited
inflate<T extends ChildHandleType>(Widget widget, {Object? id}) → T
Dynamically inflates a widget as a child of this boxy, should only be called in BoxyChild.layout.
inherited
layout() Size
Override this method to lay out children and return the final size of the boxy.
override
maxIntrinsicHeight(double width) double
Override to change the maximum height that this box could be without failing to correctly paint its contents within itself, without clipping.
inherited
maxIntrinsicWidth(double height) double
Override to change the maximum width that this box could be without failing to correctly paint its contents within itself, without clipping.
inherited
minIntrinsicHeight(double width) double
Override to change the minimum height that this box could be without failing to correctly paint its contents within itself, without clipping.
inherited
minIntrinsicWidth(double height) double
Override to change the minimum width that this box could be without failing to correctly paint its contents within itself, without clipping.
inherited
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
onPointerEvent(PointerEvent event, covariant BoxHitTestEntry entry) → void
Override to handle pointer events that hit this boxy.
inherited
paint() → void
Override this method to paint below children.
inherited
paintChildren() → void
Override this method to change how children get painted.
inherited
paintForeground() → void
Override this method to paint above children.
inherited
paintLayer(ContainerLayer layer, {VoidCallback? painter, Offset? offset, Rect? debugBounds}) → void
Paints a ContainerLayer compositing layer in the current painting context with an optional painter callback, this should only be called in paintChildren.
inherited
shouldRelayout(covariant BaseBoxyDelegate<Object, BaseBoxyChild> oldDelegate) bool
Override this method to return true when the children need to be laid out.
inherited
shouldRepaint(covariant BaseBoxyDelegate<Object, BaseBoxyChild> oldDelegate) bool
Override this method to return true when the children need to be repainted.
inherited
toString() String
Override this method to include additional information in the debugging data printed by debugDumpRenderTree and friends.
inherited

Operators

operator ==(Object other) bool
The equality operator.
inherited