Skip to content

Instantly share code, notes, and snippets.

@jxw1102
Last active February 12, 2024 07:21
Show Gist options
  • Save jxw1102/a9b58a78a80c8e2f54233b418429fa50 to your computer and use it in GitHub Desktop.
Save jxw1102/a9b58a78a80c8e2f54233b418429fa50 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'dart:math' as math;
class CustomLayout extends MultiChildRenderObjectWidget {
CustomLayout({
Key key,
List<Widget> children = const <Widget>[],
}) : super(key: key, children: children);
@override
RenderCustomLayoutBox createRenderObject(BuildContext context) {
return RenderCustomLayoutBox();
}
}
class RenderCustomLayoutBox extends RenderBox
with ContainerRenderObjectMixin<RenderBox, CustomLayoutParentData>,
RenderBoxContainerDefaultsMixin<RenderBox, CustomLayoutParentData> {
RenderCustomLayoutBox({
List<RenderBox> children,
}) {
addAll(children);
}
@override
void setupParentData(RenderBox child) {
if (child.parentData is! CustomLayoutParentData) {
child.parentData = CustomLayoutParentData();
}
}
double _getIntrinsicHeight(double childSize(RenderBox child)) {
double inflexibleSpace = 0.0;
RenderBox child = firstChild;
while (child != null) {
if (child == lastChild)
break;
inflexibleSpace += childSize(child);
final FlexParentData childParentData = child.parentData;
child = childParentData.nextSibling;
}
return inflexibleSpace;
}
double _getIntrinsicWidth(double childSize(RenderBox child)) {
double maxSpace = 0.0;
RenderBox child = firstChild;
while (child != null) {
if (child == lastChild)
break;
maxSpace = math.max(maxSpace, childSize(child));
final FlexParentData childParentData = child.parentData;
child = childParentData.nextSibling;
}
return maxSpace;
}
@override
double computeMinIntrinsicWidth(double height) {
return _getIntrinsicWidth((RenderBox child) => child.getMinIntrinsicWidth(height));
}
@override
double computeMaxIntrinsicWidth(double height) {
return _getIntrinsicWidth((RenderBox child) => child.getMaxIntrinsicWidth(height));
}
@override
double computeMinIntrinsicHeight(double width) {
return _getIntrinsicHeight((RenderBox child) => child.getMinIntrinsicHeight(width));
}
@override
double computeMaxIntrinsicHeight(double width) {
return _getIntrinsicHeight((RenderBox child) => child.getMaxIntrinsicHeight(width));
}
@override
void performLayout() {
if (childCount == 0) {
size = constraints.biggest;
assert(size.isFinite);
return;
}
double width = constraints.maxWidth;
double height = 0;
RenderBox child = firstChild;
while (child != null) {
if (child == lastChild)
break;
final CustomLayoutParentData childParentData = child.parentData;
child.layout(BoxConstraints.tightFor(width: width), parentUsesSize: true);
childParentData.offset = Offset(0, height);
final Size childSize = child.size;
width = math.max(width, childSize.width);
height += childSize.height;
child = childParentData.nextSibling;
}
size = Size(width, height);
lastChild.layout(BoxConstraints(maxWidth: width, maxHeight: height), parentUsesSize: true);
final CustomLayoutParentData childParentData = lastChild.parentData;
final double margin = 20;
final double x = size.width - lastChild.size.width - margin;
final double y = height - childParentData.previousSibling.size.height - lastChild.size.height / 2;
childParentData.offset = Offset(x, y);
}
@override
void paint(PaintingContext context, Offset offset) {
defaultPaint(context, offset);
}
@override
bool hitTestChildren(HitTestResult result, { Offset position }) {
return defaultHitTestChildren(result, position: position);
}
}
class CustomLayoutParentData extends ContainerBoxParentData<RenderBox> {
}
@gmaggio
Copy link

gmaggio commented Jan 13, 2024

Here's my update for a nullable version:

class CustomLayout extends MultiChildRenderObjectWidget {
  const CustomLayout({
    super.key,
    super.children = const <Widget>[],
  });

  @override
  RenderCustomLayoutBox createRenderObject(BuildContext context) {
    return RenderCustomLayoutBox();
  }
}

class RenderCustomLayoutBox extends RenderBox
    with
        ContainerRenderObjectMixin<RenderBox, CustomLayoutParentData>,
        RenderBoxContainerDefaultsMixin<RenderBox, CustomLayoutParentData> {
  RenderCustomLayoutBox({
    List<RenderBox>? children,
  }) {
    addAll(children);
  }

  @override
  void setupParentData(RenderBox child) {
    if (child.parentData is! CustomLayoutParentData) {
      child.parentData = CustomLayoutParentData();
    }
  }

  double _getIntrinsicHeight(double Function(RenderBox child) childSize) {
    double inflexibleSpace = 0.0;
    RenderBox? child = firstChild;
    while (child != null) {
      if (child == lastChild) break;
      inflexibleSpace += childSize(child);
      final FlexParentData childParentData = child.parentData as FlexParentData;
      child = childParentData.nextSibling;
    }
    return inflexibleSpace;
  }

  double _getIntrinsicWidth(double Function(RenderBox child) childSize) {
    double maxSpace = 0.0;
    RenderBox? child = firstChild;
    while (child != null) {
      if (child == lastChild) break;
      maxSpace = math.max(maxSpace, childSize(child));
      final FlexParentData childParentData = child.parentData as FlexParentData;
      child = childParentData.nextSibling;
    }
    return maxSpace;
  }

  @override
  double computeMinIntrinsicWidth(double height) {
    return _getIntrinsicWidth(
      (RenderBox child) => child.getMinIntrinsicWidth(height),
    );
  }

  @override
  double computeMaxIntrinsicWidth(double height) {
    return _getIntrinsicWidth(
      (RenderBox child) => child.getMaxIntrinsicWidth(height),
    );
  }

  @override
  double computeMinIntrinsicHeight(double width) {
    return _getIntrinsicHeight(
      (RenderBox child) => child.getMinIntrinsicHeight(width),
    );
  }

  @override
  double computeMaxIntrinsicHeight(double width) {
    return _getIntrinsicHeight(
      (RenderBox child) => child.getMaxIntrinsicHeight(width),
    );
  }

  @override
  void performLayout() {
    if (childCount == 0) {
      size = constraints.biggest;
      assert(size.isFinite);
      return;
    }

    double width = constraints.maxWidth;
    double height = 0;

    RenderBox? child = firstChild;
    while (child != null) {
      if (child == lastChild) break;

      final CustomLayoutParentData childParentData =
          child.parentData as CustomLayoutParentData;

      child.layout(BoxConstraints.tightFor(width: width), parentUsesSize: true);
      childParentData.offset = Offset(0, height);

      final Size childSize = child.size;
      width = math.max(width, childSize.width);
      height += childSize.height;

      child = childParentData.nextSibling;
    }

    size = Size(width, height);

    lastChild?.layout(
      BoxConstraints(maxWidth: width, maxHeight: height),
      parentUsesSize: true,
    );

    final CustomLayoutParentData? childParentData =
        lastChild?.parentData as CustomLayoutParentData?;

    const double margin = 20;

    final double x = size.width - (lastChild?.size.width ?? 0) - margin;
    final double y = height -
        (childParentData?.previousSibling?.size.height ?? 0) -
        (lastChild?.size.height ?? 0) / 2;
    childParentData?.offset = Offset(x, y);
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    defaultPaint(context, offset);
  }

  @override
  bool hitTestChildren(HitTestResult result, {required Offset position}) {
    return defaultHitTestChildren(
      result as BoxHitTestResult,
      position: position,
    );
  }
}

class CustomLayoutParentData extends ContainerBoxParentData<RenderBox> {}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment