Ejemplo n.º 1
0
        protected override void debugAssertDoesMeetConstraints()
        {
            D.assert(this.geometry.debugAssertIsValid(
                         informationCollector: (information) => {
                information.AppendLine("The RenderSliver that returned the offending geometry was:");
                information.AppendLine("  " + this.toStringShallow(joiner: "\n  "));
            }));
            D.assert(() => {
                if (this.geometry.paintExtent > this.constraints.remainingPaintExtent)
                {
                    throw new UIWidgetsError(
                        "SliverGeometry has a paintOffset that exceeds the remainingPaintExtent from the constraints.\n" +
                        "The render object whose geometry violates the constraints is the following:\n" +
                        "  " + this.toStringShallow(joiner: "\n  ") + "\n" +
                        SliverGeometry._debugCompareFloats(
                            "remainingPaintExtent", this.constraints.remainingPaintExtent,
                            "paintExtent", this.geometry.paintExtent) +
                        "The paintExtent must cause the child sliver to paint within the viewport, and so " +
                        "cannot exceed the remainingPaintExtent."
                        );
                }

                return(true);
            });
        }
Ejemplo n.º 2
0
        protected override float updateGeometry()
        {
            float?minExtent        = this.minExtent;
            float?minAllowedExtent = constraints.remainingPaintExtent > minExtent
                ? minExtent
                : constraints.remainingPaintExtent;
            float?maxExtent          = this.maxExtent;
            float?paintExtent        = maxExtent - _effectiveScrollOffset;
            float?clampedPaintExtent =
                paintExtent?.clamp(minAllowedExtent ?? 0.0f, constraints.remainingPaintExtent);
            float?layoutExtent  = maxExtent - constraints.scrollOffset;
            float?stretchOffset = stretchConfiguration != null?
                                  constraints.overlap.abs() :
                                      0.0f;

            geometry = new SliverGeometry(
                scrollExtent: maxExtent ?? 0.0f,
                paintExtent: clampedPaintExtent ?? 0.0f,
                layoutExtent: layoutExtent?.clamp(0.0f, clampedPaintExtent ?? 0.0f),
                maxPaintExtent: (maxExtent ?? 0.0f) + (stretchOffset ?? 0.0f),
                maxScrollObstructionExtent: maxExtent ?? 0.0f,
                hasVisualOverflow: true
                );
            return(0.0f);
        }
Ejemplo n.º 3
0
        protected override void debugAssertDoesMeetConstraints()
        {
            D.assert(
                () => {
                IEnumerable <DiagnosticsNode> infoCollector()
                {
                    yield return(describeForError("The RenderSliver that returned the offending geometry was"));
                }

                return(geometry.debugAssertIsValid(
                           informationCollector: infoCollector));
            });
            D.assert(() => {
                if (geometry.paintOrigin + geometry.paintExtent > constraints.remainingPaintExtent)
                {
                    List <DiagnosticsNode> diagnosticInfo = new List <DiagnosticsNode>();
                    diagnosticInfo.Add(new ErrorSummary("SliverGeometry has a paintOffset that exceeds the remainingPaintExtent from the constraints."));
                    diagnosticInfo.Add(describeForError("The render object whose geometry violates the constraints is the following:"));
                    diagnosticInfo.AddRange(SliverGeometry._debugCompareFloats("remainingPaintExtent", constraints.remainingPaintExtent,
                                                                               "paintOrigin + paintExtent", geometry.paintOrigin + geometry.paintExtent));
                    diagnosticInfo.Add(new ErrorDescription("The paintExtent must cause the child sliver to paint within the viewport, and so " +
                                                            "cannot exceed the remainingPaintExtent."));

                    throw new UIWidgetsError(diagnosticInfo);
                }

                return(true);
            });
        }
Ejemplo n.º 4
0
        protected override void performLayout()
        {
            SliverConstraints constraints = this.constraints;
            float             extent      = constraints.remainingPaintExtent - Mathf.Min(constraints.overlap, 0.0f);

            if (child != null)
            {
                child.layout(constraints.asBoxConstraints(
                                 minExtent: extent,
                                 maxExtent: extent
                                 ));
            }

            float paintedChildSize = calculatePaintOffset(constraints, from: 0.0f, to: extent);

            D.assert(paintedChildSize.isFinite());
            D.assert(paintedChildSize >= 0.0);
            geometry = new SliverGeometry(
                scrollExtent: constraints.viewportMainAxisExtent,
                paintExtent: paintedChildSize,
                maxPaintExtent: paintedChildSize,
                hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0
                );
            if (child != null)
            {
                setChildParentData(child, constraints, geometry);
            }
        }
Ejemplo n.º 5
0
 protected override void performLayout()
 {
     D.assert(child != null);
     child.layout(constraints, parentUsesSize: true);
     if (!offstage)
     {
         geometry = child.geometry;
     }
     else
     {
         geometry = new SliverGeometry(
             scrollExtent: 0.0f,
             visible: false,
             maxPaintExtent: 0.0f);
     }
 }
Ejemplo n.º 6
0
        protected override void performLayout()
        {
            float?maxExtent = this.maxExtent;

            layoutChild(constraints.scrollOffset, maxExtent ?? 0.0f);
            float?paintExtent = maxExtent - constraints.scrollOffset;

            geometry = new SliverGeometry(
                scrollExtent: maxExtent ?? 0.0f,
                paintOrigin: Mathf.Min(constraints.overlap, 0.0f),
                paintExtent: paintExtent?.clamp(0.0f, constraints.remainingPaintExtent) ?? 0.0f,
                maxPaintExtent: maxExtent ?? 0.0f,
                hasVisualOverflow: true
                );
            _childPosition = updateGeometry();
        }
Ejemplo n.º 7
0
        protected override void performLayout()
        {
            SliverConstraints constraints = this.constraints;
            float             extent      = constraints.viewportMainAxisExtent - constraints.precedingScrollExtent;

            if (child != null)
            {
                float childExtent = 0f;
                switch (constraints.axis)
                {
                case Axis.horizontal:
                    childExtent = child.getMaxIntrinsicWidth(constraints.crossAxisExtent);
                    break;

                case Axis.vertical:
                    childExtent = child.getMaxIntrinsicHeight(constraints.crossAxisExtent);
                    break;
                }
                extent = Mathf.Max(extent, childExtent);
                child.layout(constraints.asBoxConstraints(
                                 minExtent: extent,
                                 maxExtent: extent
                                 ));
            }

            D.assert(extent.isFinite(), () =>
                     "The calculated extent for the child of SliverFillRemaining is not finite. " +
                     "This can happen if the child is a scrollable, in which case, the " +
                     "hasScrollBody property of SliverFillRemaining should not be set to " +
                     "false."
                     );
            float paintedChildSize = calculatePaintOffset(constraints, from: 0.0f, to: extent);

            D.assert(paintedChildSize.isFinite());
            D.assert(paintedChildSize >= 0.0);
            geometry = new SliverGeometry(
                scrollExtent: extent,
                paintExtent: paintedChildSize,
                maxPaintExtent: paintedChildSize,
                hasVisualOverflow: extent > constraints.remainingPaintExtent || constraints.scrollOffset > 0.0
                );
            if (child != null)
            {
                setChildParentData(child, constraints, geometry);
            }
        }
Ejemplo n.º 8
0
        protected override void performLayout()
        {
            if (child == null)
            {
                geometry = SliverGeometry.zero;
                return;
            }

            SliverConstraints constraints = this.constraints;

            child.layout(constraints.asBoxConstraints(), parentUsesSize: true);

            float childExtent = 0.0f;

            switch (constraints.axis)
            {
            case Axis.horizontal:
                childExtent = child.size.width;
                break;

            case Axis.vertical:
                childExtent = child.size.height;
                break;
            }

            float paintedChildSize = calculatePaintOffset(constraints, from: 0.0f, to: childExtent);
            float cacheExtent      = calculateCacheOffset(constraints, from: 0.0f, to: childExtent);

            D.assert(paintedChildSize.isFinite());
            D.assert(paintedChildSize >= 0.0f);

            geometry = new SliverGeometry(
                scrollExtent: childExtent,
                paintExtent: paintedChildSize,
                cacheExtent: cacheExtent,
                maxPaintExtent: childExtent,
                hitTestExtent: paintedChildSize,
                hasVisualOverflow: childExtent > constraints.remainingPaintExtent ||
                constraints.scrollOffset > 0.0f
                );

            setChildParentData(child, constraints, geometry);
        }
Ejemplo n.º 9
0
        protected float updateGeometry()
        {
            float?stretchOffset = 0.0f;

            if (stretchConfiguration != null && _childPosition == 0.0)
            {
                stretchOffset += constraints.overlap.abs();
            }
            float?maxExtent   = this.maxExtent;
            float?paintExtent = maxExtent - constraints.scrollOffset;

            geometry = new SliverGeometry(
                scrollExtent: maxExtent ?? 0.0f,
                paintOrigin: Mathf.Min(constraints.overlap, 0.0f),
                paintExtent: paintExtent?.clamp(0.0f, constraints.remainingPaintExtent) ?? 0.0f,
                maxPaintExtent: maxExtent + stretchOffset ?? 0.0f,
                hasVisualOverflow: true// Conservatively say we do have overflow to avoid complexity.
                );
            return(stretchOffset > 0 ? 0.0f :Mathf.Min(0.0f, paintExtent ?? 0.0f - childExtent));
        }
Ejemplo n.º 10
0
        protected virtual float updateGeometry()
        {
            float stretchOffset = 0.0f;

            if (stretchConfiguration != null && _childPosition == 0.0)
            {
                stretchOffset += constraints.overlap.abs();
            }
            float?maxExtent    = this.maxExtent;
            float?paintExtent  = maxExtent - _effectiveScrollOffset;
            float?layoutExtent = maxExtent - constraints.scrollOffset;

            geometry = new SliverGeometry(
                scrollExtent: maxExtent ?? 0.0f,
                paintOrigin: Mathf.Min(constraints.overlap, 0.0f),
                paintExtent: paintExtent?.clamp(0.0f, constraints.remainingPaintExtent) ?? 0.0f,
                layoutExtent: layoutExtent?.clamp(0.0f, constraints.remainingPaintExtent),
                maxPaintExtent: (maxExtent ?? 0.0f) + stretchOffset,
                hasVisualOverflow: true
                );
            return(stretchOffset > 0 ? 0.0f : Mathf.Min(0.0f, paintExtent ?? 0.0f - childExtent));
        }
Ejemplo n.º 11
0
        protected override void performLayout()
        {
            SliverConstraints constraints = this.constraints;
            float?            maxExtent   = this.maxExtent;
            bool overlapsContent          = constraints.overlap > 0.0f;

            layoutChild(constraints.scrollOffset, maxExtent ?? 0.0f, overlapsContent: overlapsContent);
            float effectiveRemainingPaintExtent = Mathf.Max(0, constraints.remainingPaintExtent - constraints.overlap);
            float?layoutExtent  = (maxExtent - constraints.scrollOffset)?.clamp(0.0f, effectiveRemainingPaintExtent);
            float stretchOffset = stretchConfiguration != null?
                                  constraints.overlap.abs() :
                                      0.0f;

            geometry = new SliverGeometry(
                scrollExtent: maxExtent ?? 0.0f,
                paintOrigin: constraints.overlap,
                paintExtent: Mathf.Min(childExtent, effectiveRemainingPaintExtent),
                layoutExtent: layoutExtent,
                maxPaintExtent: (maxExtent ?? 0.0f) + stretchOffset,
                maxScrollObstructionExtent: minExtent ?? 0.0f,
                cacheExtent: layoutExtent > 0.0f ? -constraints.cacheOrigin + layoutExtent : layoutExtent,
                hasVisualOverflow: true
                );
        }
Ejemplo n.º 12
0
        protected override void performLayout()
        {
            SliverConstraints constraints = this.constraints;

            childManager.didStartLayout();
            childManager.setDidUnderflow(false);

            float itemExtent = this.itemExtent;

            float scrollOffset = constraints.scrollOffset + constraints.cacheOrigin;

            D.assert(scrollOffset >= 0.0);
            float remainingExtent = constraints.remainingCacheExtent;

            D.assert(remainingExtent >= 0.0);
            float targetEndScrollOffset = scrollOffset + remainingExtent;

            BoxConstraints childConstraints = constraints.asBoxConstraints(
                minExtent: itemExtent,
                maxExtent: itemExtent
                );

            int firstIndex      = getMinChildIndexForScrollOffset(scrollOffset, itemExtent);
            int?targetLastIndex = targetEndScrollOffset.isFinite()
                ? getMaxChildIndexForScrollOffset(targetEndScrollOffset, itemExtent)
                : (int?)null;

            if (firstChild != null)
            {
                int leadingGarbage  = _calculateLeadingGarbage(firstIndex);
                int trailingGarbage = targetLastIndex == null ? 0: _calculateTrailingGarbage(targetLastIndex.Value);
                collectGarbage(leadingGarbage, trailingGarbage);
            }
            else
            {
                collectGarbage(0, 0);
            }

            if (firstChild == null)
            {
                if (!addInitialChild(index: firstIndex,
                                     layoutOffset: indexToLayoutOffset(itemExtent, firstIndex)))
                {
                    float max;

                    if (childManager.childCount != null)
                    {
                        max = computeMaxScrollOffset(constraints, itemExtent);
                    }
                    else if (firstIndex <= 0)
                    {
                        max = 0.0f;
                    }
                    else
                    {
                        // We will have to find it manually.
                        int possibleFirstIndex = firstIndex - 1;
                        while (
                            possibleFirstIndex > 0 &&
                            !addInitialChild(
                                index: possibleFirstIndex,
                                layoutOffset: indexToLayoutOffset(itemExtent, possibleFirstIndex)
                                )
                            )
                        {
                            possibleFirstIndex -= 1;
                        }

                        max = possibleFirstIndex * itemExtent;
                    }

                    geometry = new SliverGeometry(
                        scrollExtent: max,
                        maxPaintExtent: max
                        );
                    childManager.didFinishLayout();
                    return;
                }
            }

            RenderBox trailingChildWithLayout = null;

            for (int index = indexOf(firstChild) - 1; index >= firstIndex; --index)
            {
                RenderBox child = insertAndLayoutLeadingChild(childConstraints);
                if (child == null)
                {
                    geometry = new SliverGeometry(scrollOffsetCorrection: index * itemExtent);
                    return;
                }

                var childParentData = (SliverMultiBoxAdaptorParentData)child.parentData;
                childParentData.layoutOffset = indexToLayoutOffset(itemExtent, index);
                D.assert(childParentData.index == index);
                trailingChildWithLayout = trailingChildWithLayout ?? child;
            }

            if (trailingChildWithLayout == null)
            {
                firstChild.layout(childConstraints);
                var childParentData = (SliverMultiBoxAdaptorParentData)firstChild.parentData;
                childParentData.layoutOffset = indexToLayoutOffset(itemExtent, firstIndex);
                trailingChildWithLayout      = firstChild;
            }

            float estimatedMaxScrollOffset = float.PositiveInfinity;

            for (int index = indexOf(trailingChildWithLayout) + 1;
                 targetLastIndex == null || index <= targetLastIndex;
                 ++index)
            {
                RenderBox child = childAfter(trailingChildWithLayout);
                if (child == null || indexOf(child) != index)
                {
                    child = insertAndLayoutChild(childConstraints, after: trailingChildWithLayout);
                    if (child == null)
                    {
                        estimatedMaxScrollOffset = index * itemExtent;
                        break;
                    }
                }
                else
                {
                    child.layout(childConstraints);
                }

                trailingChildWithLayout = child;
                var childParentData = (SliverMultiBoxAdaptorParentData)child.parentData;
                D.assert(childParentData.index == index);
                childParentData.layoutOffset = indexToLayoutOffset(itemExtent, childParentData.index);
            }

            int   lastIndex            = indexOf(lastChild);
            float leadingScrollOffset  = indexToLayoutOffset(itemExtent, firstIndex);
            float trailingScrollOffset = indexToLayoutOffset(itemExtent, lastIndex + 1);

            D.assert(firstIndex == 0 ||
                     childScrollOffset(firstChild) - scrollOffset <= foundation_.precisionErrorTolerance);
            D.assert(debugAssertChildListIsNonEmptyAndContiguous());
            D.assert(indexOf(firstChild) == firstIndex);
            D.assert(targetLastIndex == null || lastIndex <= targetLastIndex);


            estimatedMaxScrollOffset = Mathf.Min(
                estimatedMaxScrollOffset,
                estimateMaxScrollOffset(
                    constraints,
                    firstIndex: firstIndex,
                    lastIndex: lastIndex,
                    leadingScrollOffset: leadingScrollOffset,
                    trailingScrollOffset: trailingScrollOffset
                    )
                );

            float paintExtent = calculatePaintOffset(
                constraints,
                from: leadingScrollOffset,
                to: trailingScrollOffset
                );

            float cacheExtent = calculateCacheOffset(
                constraints,
                from: leadingScrollOffset,
                to: trailingScrollOffset
                );

            float targetEndScrollOffsetForPaint =
                constraints.scrollOffset + constraints.remainingPaintExtent;
            int?targetLastIndexForPaint = targetEndScrollOffsetForPaint.isFinite()
                ? getMaxChildIndexForScrollOffset(targetEndScrollOffsetForPaint, itemExtent)
                : (int?)null;

            geometry = new SliverGeometry(
                scrollExtent: estimatedMaxScrollOffset,
                paintExtent: paintExtent,
                cacheExtent: cacheExtent,
                maxPaintExtent: estimatedMaxScrollOffset,
                hasVisualOverflow: (targetLastIndexForPaint != null && lastIndex >= targetLastIndexForPaint) ||
                constraints.scrollOffset > 0.0
                );

            if (estimatedMaxScrollOffset == trailingScrollOffset)
            {
                childManager.setDidUnderflow(true);
            }

            childManager.didFinishLayout();
        }
Ejemplo n.º 13
0
        public void setChildParentData(RenderObject child, SliverConstraints constraints, SliverGeometry geometry)
        {
            var childParentData = (SliverPhysicalParentData)child.parentData;

            switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection(constraints.axisDirection,
                                                                             constraints.growthDirection))
            {
            case AxisDirection.up:
                childParentData.paintOffset = new Offset(0.0f,
                                                         -(geometry.scrollExtent - (geometry.paintExtent + constraints.scrollOffset)));
                break;

            case AxisDirection.right:
                childParentData.paintOffset = new Offset(-constraints.scrollOffset, 0.0f);
                break;

            case AxisDirection.down:
                childParentData.paintOffset = new Offset(0.0f, -constraints.scrollOffset);
                break;

            case AxisDirection.left:
                childParentData.paintOffset =
                    new Offset(-(geometry.scrollExtent - (geometry.paintExtent + constraints.scrollOffset)),
                               0.0f);
                break;
            }
        }
Ejemplo n.º 14
0
        protected override void performLayout()
        {
            float beforePadding    = this.beforePadding;
            float afterPadding     = this.afterPadding;
            float mainAxisPadding  = this.mainAxisPadding;
            float crossAxisPadding = this.crossAxisPadding;

            if (this.child == null)
            {
                this.geometry = new SliverGeometry(
                    scrollExtent: mainAxisPadding,
                    paintExtent: Mathf.Min(mainAxisPadding, this.constraints.remainingPaintExtent),
                    maxPaintExtent: mainAxisPadding
                    );
                return;
            }

            this.child.layout(
                this.constraints.copyWith(
                    scrollOffset: Mathf.Max(0.0f, this.constraints.scrollOffset - beforePadding),
                    cacheOrigin: Mathf.Min(0.0f, this.constraints.cacheOrigin + beforePadding),
                    overlap: 0.0f,
                    remainingPaintExtent: this.constraints.remainingPaintExtent -
                    this.calculatePaintOffset(this.constraints, from: 0.0f, to: beforePadding),
                    remainingCacheExtent: this.constraints.remainingCacheExtent -
                    this.calculateCacheOffset(this.constraints, from: 0.0f, to: beforePadding),
                    crossAxisExtent: Mathf.Max(0.0f, this.constraints.crossAxisExtent - crossAxisPadding)
                    ),
                parentUsesSize: true
                );

            SliverGeometry childLayoutGeometry = this.child.geometry;

            if (childLayoutGeometry.scrollOffsetCorrection != null)
            {
                this.geometry = new SliverGeometry(
                    scrollOffsetCorrection: childLayoutGeometry.scrollOffsetCorrection
                    );
                return;
            }

            float beforePaddingPaintExtent = this.calculatePaintOffset(
                this.constraints,
                from: 0.0f,
                to: beforePadding
                );

            float afterPaddingPaintExtent = this.calculatePaintOffset(
                this.constraints,
                from: beforePadding + childLayoutGeometry.scrollExtent,
                to: mainAxisPadding + childLayoutGeometry.scrollExtent
                );

            float mainAxisPaddingPaintExtent = beforePaddingPaintExtent + afterPaddingPaintExtent;
            float beforePaddingCacheExtent   = this.calculateCacheOffset(
                this.constraints,
                from: 0.0f,
                to: beforePadding
                );
            float afterPaddingCacheExtent = this.calculateCacheOffset(
                this.constraints,
                from: beforePadding + childLayoutGeometry.scrollExtent,
                to: mainAxisPadding + childLayoutGeometry.scrollExtent
                );

            float mainAxisPaddingCacheExtent = afterPaddingCacheExtent + beforePaddingCacheExtent;
            float paintExtent = Mathf.Min(
                beforePaddingPaintExtent + Mathf.Max(childLayoutGeometry.paintExtent,
                                                     childLayoutGeometry.layoutExtent + afterPaddingPaintExtent),
                this.constraints.remainingPaintExtent
                );

            this.geometry = new SliverGeometry(
                scrollExtent: mainAxisPadding + childLayoutGeometry.scrollExtent,
                paintExtent: paintExtent,
                layoutExtent: Mathf.Min(mainAxisPaddingPaintExtent + childLayoutGeometry.layoutExtent,
                                        paintExtent),
                cacheExtent: Mathf.Min(mainAxisPaddingCacheExtent + childLayoutGeometry.cacheExtent,
                                       this.constraints.remainingCacheExtent),
                maxPaintExtent: mainAxisPadding + childLayoutGeometry.maxPaintExtent,
                hitTestExtent: Mathf.Max(
                    mainAxisPaddingPaintExtent + childLayoutGeometry.paintExtent,
                    beforePaddingPaintExtent + childLayoutGeometry.hitTestExtent
                    ),
                hasVisualOverflow: childLayoutGeometry.hasVisualOverflow
                );

            var childParentData = (SliverPhysicalParentData)this.child.parentData;

            switch (GrowthDirectionUtils.applyGrowthDirectionToAxisDirection(this.constraints.axisDirection,
                                                                             this.constraints.growthDirection))
            {
            case AxisDirection.up:
                childParentData.paintOffset = new Offset(this._padding.left,
                                                         this.calculatePaintOffset(this.constraints,
                                                                                   from: this._padding.bottom + childLayoutGeometry.scrollExtent,
                                                                                   to: this._padding.bottom + childLayoutGeometry.scrollExtent + this._padding.top));
                break;

            case AxisDirection.right:
                childParentData.paintOffset =
                    new Offset(this.calculatePaintOffset(this.constraints, from: 0.0f, to: this._padding.left),
                               this._padding.top);
                break;

            case AxisDirection.down:
                childParentData.paintOffset = new Offset(this._padding.left,
                                                         this.calculatePaintOffset(this.constraints, from: 0.0f, to: this._padding.top));
                break;

            case AxisDirection.left:
                childParentData.paintOffset = new Offset(
                    this.calculatePaintOffset(this.constraints,
                                              from: this._padding.right + childLayoutGeometry.scrollExtent,
                                              to: this._padding.right + childLayoutGeometry.scrollExtent + this._padding.left),
                    this._padding.top);
                break;
            }

            D.assert(childParentData.paintOffset != null);
            D.assert(beforePadding == this.beforePadding);
            D.assert(afterPadding == this.afterPadding);
            D.assert(mainAxisPadding == this.mainAxisPadding);
            D.assert(crossAxisPadding == this.crossAxisPadding);
        }
Ejemplo n.º 15
0
        protected override void performLayout()
        {
            childManager.didStartLayout();
            childManager.setDidUnderflow(false);

            float scrollOffset = constraints.scrollOffset + constraints.cacheOrigin;

            D.assert(scrollOffset >= 0.0);
            float remainingExtent = constraints.remainingCacheExtent;

            D.assert(remainingExtent >= 0.0);
            float          targetEndScrollOffset = scrollOffset + remainingExtent;
            BoxConstraints childConstraints      = constraints.asBoxConstraints();
            int            leadingGarbage        = 0;
            int            trailingGarbage       = 0;
            bool           reachedEnd            = false;

            if (firstChild == null)
            {
                if (!addInitialChild())
                {
                    geometry = SliverGeometry.zero;
                    childManager.didFinishLayout();
                    return;
                }
            }

            RenderBox leadingChildWithLayout = null, trailingChildWithLayout = null;

            RenderBox earliestUsefulChild = firstChild;

            for (float earliestScrollOffset = childScrollOffset(earliestUsefulChild) ?? 0.0f;
                 earliestScrollOffset > scrollOffset;
                 earliestScrollOffset = childScrollOffset(earliestUsefulChild) ?? 0.0f)
            {
                earliestUsefulChild = insertAndLayoutLeadingChild(childConstraints, parentUsesSize: true);

                if (earliestUsefulChild == null)
                {
                    var childParentData = (SliverMultiBoxAdaptorParentData)firstChild.parentData;
                    childParentData.layoutOffset = 0.0f;

                    if (scrollOffset == 0.0)
                    {
                        earliestUsefulChild     = firstChild;
                        leadingChildWithLayout  = earliestUsefulChild;
                        trailingChildWithLayout = trailingChildWithLayout ?? earliestUsefulChild;
                        break;
                    }
                    else
                    {
                        geometry = new SliverGeometry(
                            scrollOffsetCorrection: -scrollOffset
                            );
                        return;
                    }
                }
                else
                {
                    float firstChildScrollOffset = earliestScrollOffset - paintExtentOf(firstChild);
                    if (firstChildScrollOffset < -foundation_.precisionErrorTolerance)
                    {
                        float correction = 0.0f;
                        while (earliestUsefulChild != null)
                        {
                            D.assert(firstChild == earliestUsefulChild);
                            correction         += paintExtentOf(firstChild);
                            earliestUsefulChild =
                                insertAndLayoutLeadingChild(childConstraints, parentUsesSize: true);
                        }

                        geometry = new SliverGeometry(
                            scrollOffsetCorrection: correction - earliestScrollOffset
                            );
                        var childParentData = (SliverMultiBoxAdaptorParentData)firstChild.parentData;
                        childParentData.layoutOffset = 0.0f;
                        return;
                    }
                    else
                    {
                        var childParentData = (SliverMultiBoxAdaptorParentData)earliestUsefulChild.parentData;
                        childParentData.layoutOffset = firstChildScrollOffset;
                        D.assert(earliestUsefulChild == firstChild);
                        leadingChildWithLayout  = earliestUsefulChild;
                        trailingChildWithLayout = trailingChildWithLayout ?? earliestUsefulChild;
                    }
                }
            }

            D.assert(earliestUsefulChild == firstChild);
            D.assert(childScrollOffset(earliestUsefulChild) <= scrollOffset);

            if (leadingChildWithLayout == null)
            {
                earliestUsefulChild.layout(childConstraints, parentUsesSize: true);
                leadingChildWithLayout  = earliestUsefulChild;
                trailingChildWithLayout = earliestUsefulChild;
            }

            bool      inLayoutRange   = true;
            RenderBox child           = earliestUsefulChild;
            int       index           = indexOf(child);
            float     endScrollOffset = childScrollOffset(child) + paintExtentOf(child) ?? 0.0f;

            Func <bool> advance = () => {
                D.assert(child != null);
                if (child == trailingChildWithLayout)
                {
                    inLayoutRange = false;
                }

                child = childAfter(child);
                if (child == null)
                {
                    inLayoutRange = false;
                }

                index += 1;
                if (!inLayoutRange)
                {
                    if (child == null || indexOf(child) != index)
                    {
                        child = insertAndLayoutChild(childConstraints,
                                                     after: trailingChildWithLayout,
                                                     parentUsesSize: true
                                                     );
                        if (child == null)
                        {
                            return(false);
                        }
                    }
                    else
                    {
                        child.layout(childConstraints, parentUsesSize: true);
                    }

                    trailingChildWithLayout = child;
                }

                D.assert(child != null);
                var childParentData = (SliverMultiBoxAdaptorParentData)child.parentData;
                childParentData.layoutOffset = endScrollOffset;
                D.assert(childParentData.index == index);
                endScrollOffset = childScrollOffset(child) + paintExtentOf(child) ?? 0.0f;
                return(true);
            };

            while (endScrollOffset < scrollOffset)
            {
                leadingGarbage += 1;
                if (!advance())
                {
                    D.assert(leadingGarbage == childCount);
                    D.assert(child == null);

                    collectGarbage(leadingGarbage - 1, 0);
                    D.assert(firstChild == lastChild);
                    float extent = childScrollOffset(lastChild) + paintExtentOf(lastChild) ?? 0.0f;
                    geometry = new SliverGeometry(
                        scrollExtent: extent,
                        paintExtent: 0.0f,
                        maxPaintExtent: extent
                        );
                    return;
                }
            }

            while (endScrollOffset < targetEndScrollOffset)
            {
                if (!advance())
                {
                    reachedEnd = true;
                    break;
                }
            }

            if (child != null)
            {
                child = childAfter(child);
                while (child != null)
                {
                    trailingGarbage += 1;
                    child            = childAfter(child);
                }
            }

            collectGarbage(leadingGarbage, trailingGarbage);

            D.assert(debugAssertChildListIsNonEmptyAndContiguous());

            float?estimatedMaxScrollOffset;

            if (reachedEnd)
            {
                estimatedMaxScrollOffset = endScrollOffset;
            }
            else
            {
                estimatedMaxScrollOffset = childManager.estimateMaxScrollOffset(
                    constraints,
                    firstIndex: indexOf(firstChild),
                    lastIndex: indexOf(lastChild),
                    leadingScrollOffset: childScrollOffset(firstChild) ?? 0.0f,
                    trailingScrollOffset: endScrollOffset
                    );

                D.assert(estimatedMaxScrollOffset >= endScrollOffset - childScrollOffset(firstChild));
            }

            float paintExtent = calculatePaintOffset(
                constraints,
                from: childScrollOffset(firstChild) ?? 0.0f,
                to: endScrollOffset
                );
            float cacheExtent = calculateCacheOffset(
                constraints,
                from: childScrollOffset(firstChild) ?? 0.0f,
                to: endScrollOffset
                );
            float targetEndScrollOffsetForPaint =
                constraints.scrollOffset + constraints.remainingPaintExtent;

            geometry = new SliverGeometry(
                scrollExtent: estimatedMaxScrollOffset.Value,
                paintExtent: paintExtent,
                cacheExtent: cacheExtent,
                maxPaintExtent: estimatedMaxScrollOffset.Value,
                hasVisualOverflow: endScrollOffset > targetEndScrollOffsetForPaint ||
                constraints.scrollOffset > 0.0
                );

            if (estimatedMaxScrollOffset == endScrollOffset)
            {
                childManager.setDidUnderflow(true);
            }

            childManager.didFinishLayout();
        }