Skip to content

Instantly share code, notes, and snippets.

@MobileLabStudio
Last active January 31, 2023 15:02
Show Gist options
  • Select an option

  • Save MobileLabStudio/7fe0609120ee884daab8bc0f1a58257e to your computer and use it in GitHub Desktop.

Select an option

Save MobileLabStudio/7fe0609120ee884daab8bc0f1a58257e to your computer and use it in GitHub Desktop.
Flutter widget similar to LayoutBuilder
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
class BuilderWithConstraints extends RenderObjectWidget {
const BuilderWithConstraints({super.key, required this.builder});
final LayoutWidgetBuilder builder;
@override
RenderObject createRenderObject(BuildContext context) {
return _RenderBuilderWithConstraints();
}
@override
RenderObjectElement createElement() {
return _BuilderWithConstraintsElement(this);
}
}
class _BuilderWithConstraintsElement extends RenderObjectElement {
_BuilderWithConstraintsElement(super.widget);
Element? _child;
@override
void visitChildren(ElementVisitor visitor) {
if (_child != null) {
visitor(_child!);
}
}
@override
void forgetChild(Element child) {
assert(child == _child);
_child = null;
super.forgetChild(child);
}
@override
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot); // Creates the renderObject.
renderObject
..setElement(this)
..updateCallback(_layout);
}
@override
void update(BuilderWithConstraints newWidget) {
assert(widget != newWidget);
super.update(newWidget);
assert(widget == newWidget);
renderObject
..setElement(this)
..updateCallback(_layout);
renderObject.markNeedsBuild();
}
@override
void performRebuild() {
renderObject.markNeedsBuild();
super.performRebuild();
}
@override
void unmount() {
renderObject
..setElement(null)
..updateCallback(null);
super.unmount();
}
void _layout(Constraints constraints) {
@pragma('vm:notify-debugger-on-exception')
void layoutCallback() {
Widget built;
try {
built = (widget as BuilderWithConstraints).builder(
this,
constraints as BoxConstraints,
);
debugWidgetBuilderValue(widget, built);
} catch (e, _) {
built = ErrorWidget.builder(FlutterErrorDetails(exception: e));
}
try {
_child = updateChild(_child, built, null);
assert(_child != null);
} catch (e, _) {
built = ErrorWidget.builder(FlutterErrorDetails(exception: e));
_child = updateChild(null, built, slot);
}
}
owner!.buildScope(this, layoutCallback);
}
@override
void insertRenderObjectChild(RenderObject child, Object? slot) {
final renderObject = this.renderObject;
assert(slot == null);
assert(renderObject.debugValidateChild(child));
renderObject.child = child as RenderBox;
assert(renderObject == this.renderObject);
}
@override
void moveRenderObjectChild(
RenderObject child, Object? oldSlot, Object? newSlot) {
assert(false);
}
@override
void removeRenderObjectChild(RenderObject child, Object? slot) {
final renderObject = this.renderObject;
assert(renderObject.child == child);
renderObject.child = null;
assert(renderObject == this.renderObject);
}
@override
_RenderBuilderWithConstraints get renderObject =>
super.renderObject as _RenderBuilderWithConstraints;
}
class _RenderBuilderWithConstraints extends RenderBox
with RenderObjectWithChildMixin<RenderBox> {
LayoutCallback? _layoutCallback;
bool _needBuild = false;
_BuilderWithConstraintsElement? _element;
void setElement(_BuilderWithConstraintsElement? element) {
_element = element;
}
@override
double computeMaxIntrinsicHeight(double width) {
_element?._layout(constraints);
return super.computeMaxIntrinsicHeight(width);
}
@override
double computeMinIntrinsicHeight(double width) {
_element?._layout(constraints);
return super.computeMinIntrinsicHeight(width);
}
@override
void performLayout() {
rebuildIfNecessary();
if (child != null) {
final constraints = this.constraints;
child!.layout(constraints, parentUsesSize: true);
size = constraints.constrain(child!.size);
} else {
size = constraints.biggest;
}
}
@override
void paint(PaintingContext context, Offset offset) {
if (child != null) {
child!.paint(context, offset);
}
}
void updateCallback(LayoutCallback? value) {
if (value == _layoutCallback) {
return;
}
_needBuild = true;
_layoutCallback = value;
markNeedsLayout();
}
void markNeedsBuild() {
_needBuild = true;
markNeedsLayout();
}
void rebuildIfNecessary() {
if (_needBuild) {
_needBuild = false;
invokeLayoutCallback(_layoutCallback!);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment