treeform / boxy

2D GPU rendering with a tiling atlas.
MIT License
102 stars 7 forks source link

Problem with rendering a CustomBoxy when a new item is added to an animated list #54

Closed Taco55 closed 1 year ago

Taco55 commented 1 year ago

Boxy is a great library and makes a layout possible that I could not achieve with just normal Rows and Columns. However, I encounter a problem when I add an item to an animated list (of an external library), while the initial rendering of the list does not give any problems. Hopefully some help is possible.

I get the following error after adding a new item to a list:

════════ Exception caught by rendering library ═════════════════════════════════
The following assertion was thrown during performLayout():
'package:flutter/src/rendering/object.dart': Failed assertion: line 1048 pos 12: '_debugDoingLayout': is not true.

The relevant error-causing widget was
_CustomBoxy
When the exception was thrown, this was the stack
#2      PipelineOwner._enableMutationsToDirtySubtrees
#3      RenderObject.invokeLayoutCallback
#4      InflatingRenderObjectMixin._allowSubtreeMutation
#5      InflatingRenderObjectMixin.flushInflateQueue
#6      InflatingRenderObjectMixin.performLayout.<anonymous closure>
#7      InflatingElement._wrapInflater
#8      InflatingRenderObjectMixin.performLayout
#9      RenderBoxyMixin.performLayout
#10     RenderObject.layout
#11     RenderBox.layout
#12     RenderProxyBoxMixin.performLayout
#13     RenderObject.layout
#14     RenderBox.layout
#15     RenderProxyBoxMixin.performLayout
#16     RenderObject.layout
#17     RenderBox.layout
#18     RenderProxyBoxMixin.performLayout
#19     RenderCustomPaint.performLayout
#20     RenderObject.layout
#21     RenderBox.layout
#22     RenderProxyBoxMixin.performLayout
#23     _RenderCustomClip.performLayout
#24     RenderObject.layout
#25     RenderBox.layout
#26     RenderPadding.performLayout
#27     RenderObject.layout
#28     RenderBox.layout
#29     RenderProxyBoxMixin.performLayout
#30     RenderObject.layout
#31     RenderBox.layout
#32     AnimatedRenderSliverList.measureItem.<anonymous closure>
#33     AnimatedSliverMultiBoxAdaptorElement.disposableElement.<anonymous closure>
#34     BuildOwner.lockState
#35     AnimatedSliverMultiBoxAdaptorElement.disposableElement
#36     AnimatedRenderSliverList.measureItem
#37     AnimatedRenderSliverList.measureItems.<anonymous closure>
#42     _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:192:26)
(elided 6 frames from class _AssertionError, class _Timer, dart:async, and dart:async-patch)
The following RenderObject was being processed when the exception was fired: RenderBoxy<BoxyChild>#ae96a relayoutBoundary=up8 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
RenderObject: RenderBoxy<BoxyChild>#ae96a relayoutBoundary=up8 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
    parentData: <none> (can use size)
    constraints: BoxConstraints(w=377.0, 0.0<=h<=Infinity)
    size: Size(377.0, 40.0)
    child 1: RenderPadding#43672 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
        parentData: offset=Offset(0.0, 0.0) (can use size)
        constraints: BoxConstraints(w=40.0, h=40.0)
        size: Size(40.0, 40.0)
        padding: EdgeInsets.all(8.0)
        textDirection: ltr
        child: RenderSemanticsAnnotations#f1bca NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            parentData: offset=Offset(8.0, 8.0) (can use size)
            constraints: BoxConstraints(w=24.0, h=24.0)
            size: Size(24.0, 24.0)
            child: RenderExcludeSemantics#21afb NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
                parentData: <none> (can use size)
                constraints: BoxConstraints(w=24.0, h=24.0)
                size: Size(24.0, 24.0)
                excluding: true
                child: RenderConstrainedBox#e852e NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
                    parentData: <none> (can use size)
                    constraints: BoxConstraints(w=24.0, h=24.0)
                    size: Size(24.0, 24.0)
                    additionalConstraints: BoxConstraints(w=24.0, h=24.0)
    child 2: RenderPositionedBox#c6b42 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
        parentData: offset=Offset(0.0, 0.0) (can use size)
        constraints: BoxConstraints(w=49.0, h=40.0)
        size: Size(49.0, 40.0)
        alignment: Alignment.center
        textDirection: ltr
        widthFactor: expand
        heightFactor: expand
        child: RenderParagraph#89c15 relayoutBoundary=up1 NEEDS-PAINT
            parentData: offset=Offset(0.0, 11.5) (can use size)
            constraints: BoxConstraints(0.0<=w<=49.0, 0.0<=h<=40.0)
            size: Size(49.0, 17.0)
            textAlign: start
            textDirection: ltr
            softWrap: wrapping at box width
            overflow: clip
            locale: en_US
            maxLines: unlimited
            text: TextSpan
                debugLabel: (englishLike bodyMedium 2014).merge(blackCupertino bodyMedium)
                inherit: false
                color: Color(0xdd000000)
                family: .SF UI Text
                size: 14.0
                weight: 400
                baseline: alphabetic
                decoration: TextDecoration.none
                "index 4"
════════════════════════════════════════════════════════════════════════════════

The (simplified) item tiles are created as follows:

  Widget itemTile(int index) {
    return Card(
      margin: const EdgeInsets.all(8),
      child: CustomBoxy(delegate: TileBoxy(widgetSpacing: 10), children: [
        const Icon(Icons.note),
        Center(child: Text("index $index"))
      ]),
    );
  }
}

With the (simplified) delegate defined as:

class TileBoxy extends BoxyDelegate {
  final double widgetSpacing;

  TileBoxy({this.widgetSpacing = 10});

  @override
  Size layout() {
    List<double> childrenWidths = [];
    for (final child in children) {
      childrenWidths.add(min(constraints.maxWidth,
          child.render.getMaxIntrinsicWidth(double.infinity)));
    }
    var totalChildrenWidth = childrenWidths.reduce((a, b) => a + b);

    var childHeight = 0.0;
    for (var i = 0; i < children.length; i++) {
      childHeight = max(
        childHeight,
        children[i].render.getMinIntrinsicHeight(childrenWidths[i]),
      );
    }

    for (var i = 0; i < children.length; i++) {
      children[i]
          .layout(BoxConstraints.tight(Size(childrenWidths[i], childHeight)));
    }

    var x = 0.0;
    for (var i = 0; i < children.length; i++) {
      children[i].position(Offset(x, 0));
      x += childrenWidths[i] + widgetSpacing;
    }

    return Size(min(totalChildrenWidth, constraints.maxWidth), childHeight);
  }
}

Possibly this problem might be caused by the external library for the animated list (great_list_view). However, the error only occurs when I use CustomBoxy and does not occur when using a standard Row.

Is there something I need to add in the BoxyDelegate to get rid of this error?

treeform commented 1 year ago

I think you got the wrong library. This boxy has nothing to do with Dart or Layout. I think you want https://github.com/PixelToast/boxy

Taco55 commented 1 year ago

I am so sorry. Indeed, I needed the one as indicated.