| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/rendering.dart'; |
| |
| import 'framework.dart'; |
| |
| /// A bridge from a [RenderObject] to an [Element] tree. |
| /// |
| /// The given container is the [RenderObject] that the [Element] tree should be |
| /// inserted into. It must be a [RenderObject] that implements the |
| /// [RenderObjectWithChildMixin] protocol. The type argument `T` is the kind of |
| /// [RenderObject] that the container expects as its child. |
| /// |
| /// The [RenderObjectToWidgetAdapter] is an alternative to [RootWidget] for |
| /// bootstrapping an element tree. Unlike [RootWidget] it requires the |
| /// existence of a render tree (the [container]) to attach the element tree to. |
| class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget { |
| /// Creates a bridge from a [RenderObject] to an [Element] tree. |
| RenderObjectToWidgetAdapter({ |
| this.child, |
| required this.container, |
| this.debugShortDescription, |
| }) : super(key: GlobalObjectKey(container)); |
| |
| /// The widget below this widget in the tree. |
| /// |
| /// {@macro flutter.widgets.ProxyWidget.child} |
| final Widget? child; |
| |
| /// The [RenderObject] that is the parent of the [Element] created by this widget. |
| final RenderObjectWithChildMixin<T> container; |
| |
| /// A short description of this widget used by debugging aids. |
| final String? debugShortDescription; |
| |
| @override |
| RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this); |
| |
| @override |
| RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container; |
| |
| @override |
| void updateRenderObject(BuildContext context, RenderObject renderObject) { } |
| |
| /// Inflate this widget and actually set the resulting [RenderObject] as the |
| /// child of [container]. |
| /// |
| /// If `element` is null, this function will create a new element. Otherwise, |
| /// the given element will have an update scheduled to switch to this widget. |
| RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T>? element ]) { |
| if (element == null) { |
| owner.lockState(() { |
| element = createElement(); |
| assert(element != null); |
| element!.assignOwner(owner); |
| }); |
| owner.buildScope(element!, () { |
| element!.mount(null, null); |
| }); |
| } else { |
| element._newWidget = this; |
| element.markNeedsBuild(); |
| } |
| return element!; |
| } |
| |
| @override |
| String toStringShort() => debugShortDescription ?? super.toStringShort(); |
| } |
| |
| /// The root of an element tree that is hosted by a [RenderObject]. |
| /// |
| /// This element class is the instantiation of a [RenderObjectToWidgetAdapter] |
| /// widget. It can be used only as the root of an [Element] tree (it cannot be |
| /// mounted into another [Element]; it's parent must be null). |
| /// |
| /// In typical usage, it will be instantiated for a [RenderObjectToWidgetAdapter] |
| /// whose container is the [RenderView]. |
| class RenderObjectToWidgetElement<T extends RenderObject> extends RenderTreeRootElement with RootElementMixin { |
| /// Creates an element that is hosted by a [RenderObject]. |
| /// |
| /// The [RenderObject] created by this element is not automatically set as a |
| /// child of the hosting [RenderObject]. To actually attach this element to |
| /// the render tree, call [RenderObjectToWidgetAdapter.attachToRenderTree]. |
| RenderObjectToWidgetElement(RenderObjectToWidgetAdapter<T> super.widget); |
| |
| Element? _child; |
| |
| static const Object _rootChildSlot = Object(); |
| |
| @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) { |
| assert(parent == null); |
| super.mount(parent, newSlot); |
| _rebuild(); |
| assert(_child != null); |
| } |
| |
| @override |
| void update(RenderObjectToWidgetAdapter<T> newWidget) { |
| super.update(newWidget); |
| assert(widget == newWidget); |
| _rebuild(); |
| } |
| |
| // When we are assigned a new widget, we store it here |
| // until we are ready to update to it. |
| Widget? _newWidget; |
| |
| @override |
| void performRebuild() { |
| if (_newWidget != null) { |
| // _newWidget can be null if, for instance, we were rebuilt |
| // due to a reassemble. |
| final Widget newWidget = _newWidget!; |
| _newWidget = null; |
| update(newWidget as RenderObjectToWidgetAdapter<T>); |
| } |
| super.performRebuild(); |
| assert(_newWidget == null); |
| } |
| |
| @pragma('vm:notify-debugger-on-exception') |
| void _rebuild() { |
| try { |
| _child = updateChild(_child, (widget as RenderObjectToWidgetAdapter<T>).child, _rootChildSlot); |
| } catch (exception, stack) { |
| final FlutterErrorDetails details = FlutterErrorDetails( |
| exception: exception, |
| stack: stack, |
| library: 'widgets library', |
| context: ErrorDescription('attaching to the render tree'), |
| ); |
| FlutterError.reportError(details); |
| final Widget error = ErrorWidget.builder(details); |
| _child = updateChild(null, error, _rootChildSlot); |
| } |
| } |
| |
| @override |
| RenderObjectWithChildMixin<T> get renderObject => super.renderObject as RenderObjectWithChildMixin<T>; |
| |
| @override |
| void insertRenderObjectChild(RenderObject child, Object? slot) { |
| assert(slot == _rootChildSlot); |
| assert(renderObject.debugValidateChild(child)); |
| renderObject.child = child as T; |
| } |
| |
| @override |
| void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) { |
| assert(false); |
| } |
| |
| @override |
| void removeRenderObjectChild(RenderObject child, Object? slot) { |
| assert(renderObject.child == child); |
| renderObject.child = null; |
| } |
| } |