Last active
January 31, 2023 15:02
-
-
Save MobileLabStudio/7fe0609120ee884daab8bc0f1a58257e to your computer and use it in GitHub Desktop.
Flutter widget similar to LayoutBuilder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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