Revert "Mixin for slotted RenderObjectWidgets and RenderBox (#94077)" (#94620)
This reverts commit 988959dad9b6f07086d5a0903b1fb17ce3d00188.
diff --git a/examples/api/lib/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart b/examples/api/lib/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart
deleted file mode 100644
index 217544b..0000000
--- a/examples/api/lib/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart
+++ /dev/null
@@ -1,289 +0,0 @@
-// 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/material.dart';
-import 'package:flutter/rendering.dart';
-
-/// Slots used for the children of [Diagonal] and [RenderDiagonal].
-enum DiagonalSlot {
- topLeft,
- bottomRight,
-}
-
-/// A widget that demonstrates the usage of [SlottedMultiChildRenderObjectWidgetMixin]
-/// by providing slots for two children that will be arranged diagonally.
-class Diagonal extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<DiagonalSlot> {
- const Diagonal({
- Key? key,
- this.topLeft,
- this.bottomRight,
- this.backgroundColor,
- }) : super(key: key);
-
- final Widget? topLeft;
- final Widget? bottomRight;
- final Color? backgroundColor;
-
- @override
- Iterable<DiagonalSlot> get slots => DiagonalSlot.values;
-
- @override
- Widget? childForSlot(DiagonalSlot slot) {
- switch (slot) {
- case DiagonalSlot.topLeft:
- return topLeft;
- case DiagonalSlot.bottomRight:
- return bottomRight;
- }
- }
-
- // The [createRenderObject] and [updateRenderObject] methods configure the
- // [RenderObject] backing this widget with the configuration of the widget.
- // They do not need to do anything with the children of the widget, though.
- // The children of the widget are automatically configured on the
- // [RenderObject] by [SlottedRenderObjectElement.mount] and
- // [SlottedRenderObjectElement.update].
-
- @override
- SlottedContainerRenderObjectMixin<DiagonalSlot> createRenderObject(
- BuildContext context,
- ) {
- return RenderDiagonal(
- backgroundColor: backgroundColor,
- );
- }
-
- @override
- void updateRenderObject(
- BuildContext context,
- SlottedContainerRenderObjectMixin<DiagonalSlot> renderObject,
- ) {
- (renderObject as RenderDiagonal).backgroundColor = backgroundColor;
- }
-}
-
-/// A render object that demonstrates the usage of [SlottedContainerRenderObjectMixin]
-/// by providing slots for two children that will be arranged diagonally.
-class RenderDiagonal extends RenderBox with SlottedContainerRenderObjectMixin<DiagonalSlot>, DebugOverflowIndicatorMixin {
- RenderDiagonal({Color? backgroundColor}) : _backgroundColor = backgroundColor;
-
- // Getters and setters to configure the [RenderObject] with the configuration
- // of the [Widget]. These mostly contain boilerplate code, but depending on
- // where the configuration value is used, the setter has to call
- // [markNeedsLayout], [markNeedsPaint], or [markNeedsSemanticsUpdate].
- Color? get backgroundColor => _backgroundColor;
- Color? _backgroundColor;
- set backgroundColor(Color? value) {
- assert(value != null);
- if (_backgroundColor == value) {
- return;
- }
- _backgroundColor = value;
- markNeedsPaint();
- }
-
- // Getters to simplify accessing the slotted children.
- RenderBox? get _topLeft => childForSlot(DiagonalSlot.topLeft);
- RenderBox? get _bottomRight => childForSlot(DiagonalSlot.bottomRight);
-
- // The size this render object would have if the incoming constraints were
- // unconstrained; calculated during performLayout used during paint for an
- // assertion that checks for unintended overflow.
- late Size _childrenSize;
-
- // Returns children in hit test order.
- @override
- Iterable<RenderBox> get children sync* {
- if (_topLeft != null) {
- yield _topLeft!;
- }
- if (_bottomRight != null) {
- yield _bottomRight!;
- }
- }
-
- // LAYOUT
-
- @override
- void performLayout() {
- // Children are allowed to be as big as they want (= unconstrained).
- const BoxConstraints childConstraints = BoxConstraints();
-
- // Lay out the top left child and position it at offset zero.
- Size topLeftSize = Size.zero;
- final RenderBox? topLeft = _topLeft;
- if (topLeft != null) {
- topLeft.layout(childConstraints, parentUsesSize: true);
- _positionChild(topLeft, Offset.zero);
- topLeftSize = topLeft.size;
- }
-
- // Lay out the bottom right child and position it at the bottom right corner
- // of the top left child.
- Size bottomRightSize = Size.zero;
- final RenderBox? bottomRight = _bottomRight;
- if (bottomRight != null) {
- bottomRight.layout(childConstraints, parentUsesSize: true);
- _positionChild(
- bottomRight,
- Offset(topLeftSize.width, topLeftSize.height),
- );
- bottomRightSize = bottomRight.size;
- }
-
- // Calculate the overall size and constrain it to the given constraints.
- // Any overflow is marked (in debug mode) during paint.
- _childrenSize = Size(
- topLeftSize.width + bottomRightSize.width,
- topLeftSize.height + bottomRightSize.height,
- );
- size = constraints.constrain(_childrenSize);
- }
-
- void _positionChild(RenderBox child, Offset offset) {
- (child.parentData! as BoxParentData).offset = offset;
- }
-
- // PAINT
-
- @override
- void paint(PaintingContext context, Offset offset) {
- // Paint the background.
- if (backgroundColor != null) {
- context.canvas.drawRect(
- offset & size,
- Paint()
- ..color = backgroundColor!,
- );
- }
-
- void paintChild(RenderBox child, PaintingContext context, Offset offset) {
- final BoxParentData childParentData = child.parentData! as BoxParentData;
- context.paintChild(child, childParentData.offset + offset);
- }
-
- // Paint the children at the offset calculated during layout.
- final RenderBox? topLeft = _topLeft;
- if (topLeft != null) {
- paintChild(topLeft, context, offset);
- }
- final RenderBox? bottomRight = _bottomRight;
- if (bottomRight != null) {
- paintChild(bottomRight, context, offset);
- }
-
- // Paint an overflow indicator in debug mode if the children want to be
- // larger than the incoming constraints allow.
- assert(() {
- paintOverflowIndicator(
- context,
- offset,
- Offset.zero & size,
- Offset.zero & _childrenSize,
- );
- return true;
- }());
- }
-
- // HIT TEST
-
- @override
- bool hitTestChildren(BoxHitTestResult result, { required Offset position }) {
- for (final RenderBox child in children) {
- final BoxParentData parentData = child.parentData! as BoxParentData;
- final bool isHit = result.addWithPaintOffset(
- offset: parentData.offset,
- position: position,
- hitTest: (BoxHitTestResult result, Offset transformed) {
- assert(transformed == position - parentData.offset);
- return child.hitTest(result, position: transformed);
- },
- );
- if (isHit) {
- return true;
- }
- }
- return false;
- }
-
- // INTRINSICS
-
- // Incoming height/width are ignored as children are always laid out unconstrained.
-
- @override
- double computeMinIntrinsicWidth(double height) {
- final double topLeftWidth = _topLeft?.getMinIntrinsicWidth(double.infinity) ?? 0;
- final double bottomRightWith = _bottomRight?.getMinIntrinsicWidth(double.infinity) ?? 0;
- return topLeftWidth + bottomRightWith;
- }
-
- @override
- double computeMaxIntrinsicWidth(double height) {
- final double topLeftWidth = _topLeft?.getMaxIntrinsicWidth(double.infinity) ?? 0;
- final double bottomRightWith = _bottomRight?.getMaxIntrinsicWidth(double.infinity) ?? 0;
- return topLeftWidth + bottomRightWith; }
-
- @override
- double computeMinIntrinsicHeight(double width) {
- final double topLeftHeight = _topLeft?.getMinIntrinsicHeight(double.infinity) ?? 0;
- final double bottomRightHeight = _bottomRight?.getMinIntrinsicHeight(double.infinity) ?? 0;
- return topLeftHeight + bottomRightHeight;
- }
-
- @override
- double computeMaxIntrinsicHeight(double width) {
- final double topLeftHeight = _topLeft?.getMaxIntrinsicHeight(double.infinity) ?? 0;
- final double bottomRightHeight = _bottomRight?.getMaxIntrinsicHeight(double.infinity) ?? 0;
- return topLeftHeight + bottomRightHeight;
- }
-
- @override
- Size computeDryLayout(BoxConstraints constraints) {
- const BoxConstraints childConstraints = BoxConstraints();
- final Size topLeftSize = _topLeft?.computeDryLayout(childConstraints) ?? Size.zero;
- final Size bottomRightSize = _bottomRight?.computeDryLayout(childConstraints) ?? Size.zero;
- return constraints.constrain(Size(
- topLeftSize.width + bottomRightSize.width,
- topLeftSize.height + bottomRightSize.height,
- ));
- }
-}
-
-class ExampleWidget extends StatelessWidget {
- const ExampleWidget({Key? key}) : super(key: key);
-
- @override
- Widget build(BuildContext context) {
- return MaterialApp(
- home: Scaffold(
- appBar: AppBar(title: const Text('Slotted RenderObject Example')),
- body: Center(
- child: Diagonal(
- topLeft: Container(
- color: Colors.green,
- height: 100,
- width: 200,
- child: const Center(
- child: Text('topLeft'),
- ),
- ),
- bottomRight: Container(
- color: Colors.yellow,
- height: 60,
- width: 30,
- child: const Center(
- child: Text('bottomRight'),
- ),
- ),
- backgroundColor: Colors.blue,
- ),
- ),
- ),
- );
- }
-}
-
-void main() {
- runApp(const ExampleWidget());
-}
diff --git a/examples/api/test/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0_test.dart b/examples/api/test/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0_test.dart
deleted file mode 100644
index 150d9e5..0000000
--- a/examples/api/test/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0_test.dart
+++ /dev/null
@@ -1,41 +0,0 @@
-// 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/widgets.dart';
-import 'package:flutter_api_samples/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart' as example;
-import 'package:flutter_test/flutter_test.dart';
-
-void main() {
- testWidgets('shows two widgets arranged diagonally', (WidgetTester tester) async {
- await tester.pumpWidget(
- const example.ExampleWidget(),
- );
-
- expect(find.text('topLeft'), findsOneWidget);
- expect(find.text('bottomRight'), findsOneWidget);
-
- expect(
- tester.getBottomRight(findContainerWithText('topLeft')),
- tester.getTopLeft(findContainerWithText('bottomRight')),
- );
-
- expect(
- tester.getSize(findContainerWithText('topLeft')),
- const Size(200, 100),
- );
- expect(
- tester.getSize(findContainerWithText('bottomRight')),
- const Size(30, 60),
- );
-
- expect(
- tester.getSize(find.byType(example.Diagonal)),
- const Size(200 + 30, 100 + 60),
- );
- });
-}
-
-Finder findContainerWithText(String text) {
- return find.ancestor(of: find.text(text), matching: find.byType(Container));
-}
diff --git a/packages/flutter/lib/src/material/chip.dart b/packages/flutter/lib/src/material/chip.dart
index b720b85..82ff6c2 100644
--- a/packages/flutter/lib/src/material/chip.dart
+++ b/packages/flutter/lib/src/material/chip.dart
@@ -2059,7 +2059,7 @@
}
}
-class _ChipRenderWidget extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<_ChipSlot> {
+class _ChipRenderWidget extends RenderObjectWidget {
const _ChipRenderWidget({
Key? key,
required this.theme,
@@ -2083,19 +2083,7 @@
final ShapeBorder? avatarBorder;
@override
- Iterable<_ChipSlot> get slots => _ChipSlot.values;
-
- @override
- Widget? childForSlot(_ChipSlot slot) {
- switch (slot) {
- case _ChipSlot.label:
- return theme.label;
- case _ChipSlot.avatar:
- return theme.avatar;
- case _ChipSlot.deleteIcon:
- return theme.deleteIcon;
- }
- }
+ _RenderChipElement createElement() => _RenderChipElement(this);
@override
void updateRenderObject(BuildContext context, _RenderChip renderObject) {
@@ -2112,7 +2100,7 @@
}
@override
- SlottedContainerRenderObjectMixin<_ChipSlot> createRenderObject(BuildContext context) {
+ RenderObject createRenderObject(BuildContext context) {
return _RenderChip(
theme: theme,
textDirection: Directionality.of(context),
@@ -2133,6 +2121,105 @@
deleteIcon,
}
+class _RenderChipElement extends RenderObjectElement {
+ _RenderChipElement(_ChipRenderWidget chip) : super(chip);
+
+ final Map<_ChipSlot, Element> slotToChild = <_ChipSlot, Element>{};
+
+ @override
+ _ChipRenderWidget get widget => super.widget as _ChipRenderWidget;
+
+ @override
+ _RenderChip get renderObject => super.renderObject as _RenderChip;
+
+ @override
+ void visitChildren(ElementVisitor visitor) {
+ slotToChild.values.forEach(visitor);
+ }
+
+ @override
+ void forgetChild(Element child) {
+ assert(slotToChild.containsValue(child));
+ assert(child.slot is _ChipSlot);
+ assert(slotToChild.containsKey(child.slot));
+ slotToChild.remove(child.slot);
+ super.forgetChild(child);
+ }
+
+ void _mountChild(Widget widget, _ChipSlot slot) {
+ final Element? oldChild = slotToChild[slot];
+ final Element? newChild = updateChild(oldChild, widget, slot);
+ if (oldChild != null) {
+ slotToChild.remove(slot);
+ }
+ if (newChild != null) {
+ slotToChild[slot] = newChild;
+ }
+ }
+
+ @override
+ void mount(Element? parent, Object? newSlot) {
+ super.mount(parent, newSlot);
+ _mountChild(widget.theme.avatar, _ChipSlot.avatar);
+ _mountChild(widget.theme.deleteIcon, _ChipSlot.deleteIcon);
+ _mountChild(widget.theme.label, _ChipSlot.label);
+ }
+
+ void _updateChild(Widget widget, _ChipSlot slot) {
+ final Element? oldChild = slotToChild[slot];
+ final Element? newChild = updateChild(oldChild, widget, slot);
+ if (oldChild != null) {
+ slotToChild.remove(slot);
+ }
+ if (newChild != null) {
+ slotToChild[slot] = newChild;
+ }
+ }
+
+ @override
+ void update(_ChipRenderWidget newWidget) {
+ super.update(newWidget);
+ assert(widget == newWidget);
+ _updateChild(widget.theme.label, _ChipSlot.label);
+ _updateChild(widget.theme.avatar, _ChipSlot.avatar);
+ _updateChild(widget.theme.deleteIcon, _ChipSlot.deleteIcon);
+ }
+
+ void _updateRenderObject(RenderObject? child, _ChipSlot slot) {
+ switch (slot) {
+ case _ChipSlot.avatar:
+ renderObject.avatar = child as RenderBox?;
+ break;
+ case _ChipSlot.label:
+ renderObject.label = child as RenderBox?;
+ break;
+ case _ChipSlot.deleteIcon:
+ renderObject.deleteIcon = child as RenderBox?;
+ break;
+ }
+ }
+
+ @override
+ void insertRenderObjectChild(RenderObject child, _ChipSlot slot) {
+ assert(child is RenderBox);
+ _updateRenderObject(child, slot);
+ assert(renderObject.children.keys.contains(slot));
+ }
+
+ @override
+ void removeRenderObjectChild(RenderObject child, _ChipSlot slot) {
+ assert(child is RenderBox);
+ assert(renderObject.children[slot] == child);
+ _updateRenderObject(null, slot);
+ assert(!renderObject.children.keys.contains(slot));
+ }
+
+ @override
+ void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) {
+ assert(false, 'not reachable');
+ }
+}
+
@immutable
class _ChipRenderTheme {
const _ChipRenderTheme({
@@ -2199,7 +2286,7 @@
}
}
-class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_ChipSlot> {
+class _RenderChip extends RenderBox {
_RenderChip({
required _ChipRenderTheme theme,
required TextDirection textDirection,
@@ -2220,6 +2307,8 @@
enableAnimation.addListener(markNeedsPaint);
}
+ final Map<_ChipSlot, RenderBox> children = <_ChipSlot, RenderBox>{};
+
bool? value;
bool? isEnabled;
late Rect _deleteButtonRect;
@@ -2230,9 +2319,35 @@
Animation<double> enableAnimation;
ShapeBorder? avatarBorder;
- RenderBox? get avatar => childForSlot(_ChipSlot.avatar);
- RenderBox? get deleteIcon => childForSlot(_ChipSlot.deleteIcon);
- RenderBox? get label => childForSlot(_ChipSlot.label);
+ RenderBox? _updateChild(RenderBox? oldChild, RenderBox? newChild, _ChipSlot slot) {
+ if (oldChild != null) {
+ dropChild(oldChild);
+ children.remove(slot);
+ }
+ if (newChild != null) {
+ children[slot] = newChild;
+ adoptChild(newChild);
+ }
+ return newChild;
+ }
+
+ RenderBox? _avatar;
+ RenderBox? get avatar => _avatar;
+ set avatar(RenderBox? value) {
+ _avatar = _updateChild(_avatar, value, _ChipSlot.avatar);
+ }
+
+ RenderBox? _deleteIcon;
+ RenderBox? get deleteIcon => _deleteIcon;
+ set deleteIcon(RenderBox? value) {
+ _deleteIcon = _updateChild(_deleteIcon, value, _ChipSlot.deleteIcon);
+ }
+
+ RenderBox? _label;
+ RenderBox? get label => _label;
+ set label(RenderBox? value) {
+ _label = _updateChild(_label, value, _ChipSlot.label);
+ }
_ChipRenderTheme get theme => _theme;
_ChipRenderTheme _theme;
@@ -2255,8 +2370,7 @@
}
// The returned list is ordered for hit testing.
- @override
- Iterable<RenderBox> get children sync* {
+ Iterable<RenderBox> get _children sync* {
if (avatar != null) {
yield avatar!;
}
@@ -2272,6 +2386,47 @@
bool get deleteIconShowing => !deleteDrawerAnimation.isDismissed;
@override
+ void attach(PipelineOwner owner) {
+ super.attach(owner);
+ for (final RenderBox child in _children) {
+ child.attach(owner);
+ }
+ }
+
+ @override
+ void detach() {
+ super.detach();
+ for (final RenderBox child in _children) {
+ child.detach();
+ }
+ }
+
+ @override
+ void redepthChildren() {
+ _children.forEach(redepthChild);
+ }
+
+ @override
+ void visitChildren(RenderObjectVisitor visitor) {
+ _children.forEach(visitor);
+ }
+
+ @override
+ List<DiagnosticsNode> debugDescribeChildren() {
+ final List<DiagnosticsNode> value = <DiagnosticsNode>[];
+ void add(RenderBox? child, String name) {
+ if (child != null) {
+ value.add(child.toDiagnosticsNode(name: name));
+ }
+ }
+
+ add(avatar, 'avatar');
+ add(label, 'label');
+ add(deleteIcon, 'deleteIcon');
+ return value;
+ }
+
+ @override
bool get sizedByParent => false;
static double _minWidth(RenderBox? box, double height) {
diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart
index 7e9d5e9..ba9d7ec 100644
--- a/packages/flutter/lib/src/material/input_decorator.dart
+++ b/packages/flutter/lib/src/material/input_decorator.dart
@@ -682,7 +682,7 @@
}
// The workhorse: layout and paint a _Decorator widget's _Decoration.
-class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin<_DecorationSlot> {
+class _RenderDecoration extends RenderBox {
_RenderDecoration({
required _Decoration decoration,
required TextDirection textDirection,
@@ -702,22 +702,88 @@
_expands = expands;
static const double subtextGap = 8.0;
+ final Map<_DecorationSlot, RenderBox> children = <_DecorationSlot, RenderBox>{};
- RenderBox? get icon => childForSlot(_DecorationSlot.icon);
- RenderBox? get input => childForSlot(_DecorationSlot.input);
- RenderBox? get label => childForSlot(_DecorationSlot.label);
- RenderBox? get hint => childForSlot(_DecorationSlot.hint);
- RenderBox? get prefix => childForSlot(_DecorationSlot.prefix);
- RenderBox? get suffix => childForSlot(_DecorationSlot.suffix);
- RenderBox? get prefixIcon => childForSlot(_DecorationSlot.prefixIcon);
- RenderBox? get suffixIcon => childForSlot(_DecorationSlot.suffixIcon);
- RenderBox? get helperError => childForSlot(_DecorationSlot.helperError);
- RenderBox? get counter => childForSlot(_DecorationSlot.counter);
- RenderBox? get container => childForSlot(_DecorationSlot.container);
+ RenderBox? _updateChild(RenderBox? oldChild, RenderBox? newChild, _DecorationSlot slot) {
+ if (oldChild != null) {
+ dropChild(oldChild);
+ children.remove(slot);
+ }
+ if (newChild != null) {
+ children[slot] = newChild;
+ adoptChild(newChild);
+ }
+ return newChild;
+ }
+
+ RenderBox? _icon;
+ RenderBox? get icon => _icon;
+ set icon(RenderBox? value) {
+ _icon = _updateChild(_icon, value, _DecorationSlot.icon);
+ }
+
+ RenderBox? _input;
+ RenderBox? get input => _input;
+ set input(RenderBox? value) {
+ _input = _updateChild(_input, value, _DecorationSlot.input);
+ }
+
+ RenderBox? _label;
+ RenderBox? get label => _label;
+ set label(RenderBox? value) {
+ _label = _updateChild(_label, value, _DecorationSlot.label);
+ }
+
+ RenderBox? _hint;
+ RenderBox? get hint => _hint;
+ set hint(RenderBox? value) {
+ _hint = _updateChild(_hint, value, _DecorationSlot.hint);
+ }
+
+ RenderBox? _prefix;
+ RenderBox? get prefix => _prefix;
+ set prefix(RenderBox? value) {
+ _prefix = _updateChild(_prefix, value, _DecorationSlot.prefix);
+ }
+
+ RenderBox? _suffix;
+ RenderBox? get suffix => _suffix;
+ set suffix(RenderBox? value) {
+ _suffix = _updateChild(_suffix, value, _DecorationSlot.suffix);
+ }
+
+ RenderBox? _prefixIcon;
+ RenderBox? get prefixIcon => _prefixIcon;
+ set prefixIcon(RenderBox? value) {
+ _prefixIcon = _updateChild(_prefixIcon, value, _DecorationSlot.prefixIcon);
+ }
+
+ RenderBox? _suffixIcon;
+ RenderBox? get suffixIcon => _suffixIcon;
+ set suffixIcon(RenderBox? value) {
+ _suffixIcon = _updateChild(_suffixIcon, value, _DecorationSlot.suffixIcon);
+ }
+
+ RenderBox? _helperError;
+ RenderBox? get helperError => _helperError;
+ set helperError(RenderBox? value) {
+ _helperError = _updateChild(_helperError, value, _DecorationSlot.helperError);
+ }
+
+ RenderBox? _counter;
+ RenderBox? get counter => _counter;
+ set counter(RenderBox? value) {
+ _counter = _updateChild(_counter, value, _DecorationSlot.counter);
+ }
+
+ RenderBox? _container;
+ RenderBox? get container => _container;
+ set container(RenderBox? value) {
+ _container = _updateChild(_container, value, _DecorationSlot.container);
+ }
// The returned list is ordered for hit testing.
- @override
- Iterable<RenderBox> get children sync* {
+ Iterable<RenderBox> get _children sync* {
if (icon != null)
yield icon!;
if (input != null)
@@ -817,6 +883,30 @@
}
@override
+ void attach(PipelineOwner owner) {
+ super.attach(owner);
+ for (final RenderBox child in _children)
+ child.attach(owner);
+ }
+
+ @override
+ void detach() {
+ super.detach();
+ for (final RenderBox child in _children)
+ child.detach();
+ }
+
+ @override
+ void redepthChildren() {
+ _children.forEach(redepthChild);
+ }
+
+ @override
+ void visitChildren(RenderObjectVisitor visitor) {
+ _children.forEach(visitor);
+ }
+
+ @override
void visitChildrenForSemantics(RenderObjectVisitor visitor) {
if (icon != null)
visitor(icon!);
@@ -851,6 +941,27 @@
}
@override
+ List<DiagnosticsNode> debugDescribeChildren() {
+ final List<DiagnosticsNode> value = <DiagnosticsNode>[];
+ void add(RenderBox? child, String name) {
+ if (child != null)
+ value.add(child.toDiagnosticsNode(name: name));
+ }
+ add(icon, 'icon');
+ add(input, 'input');
+ add(label, 'label');
+ add(hint, 'hint');
+ add(prefix, 'prefix');
+ add(suffix, 'suffix');
+ add(prefixIcon, 'prefixIcon');
+ add(suffixIcon, 'suffixIcon');
+ add(helperError, 'helperError');
+ add(counter, 'counter');
+ add(container, 'container');
+ return value;
+ }
+
+ @override
bool get sizedByParent => false;
static double _minWidth(RenderBox? box, double height) {
@@ -1527,7 +1638,7 @@
@override
bool hitTestChildren(BoxHitTestResult result, { required Offset position }) {
assert(position != null);
- for (final RenderBox child in children) {
+ for (final RenderBox child in _children) {
// The label must be handled specially since we've transformed it.
final Offset offset = _boxParentData(child).offset;
final bool isHit = result.addWithPaintOffset(
@@ -1556,7 +1667,146 @@
}
}
-class _Decorator extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<_DecorationSlot> {
+class _DecorationElement extends RenderObjectElement {
+ _DecorationElement(_Decorator widget) : super(widget);
+
+ final Map<_DecorationSlot, Element> slotToChild = <_DecorationSlot, Element>{};
+
+ @override
+ _Decorator get widget => super.widget as _Decorator;
+
+ @override
+ _RenderDecoration get renderObject => super.renderObject as _RenderDecoration;
+
+ @override
+ void visitChildren(ElementVisitor visitor) {
+ slotToChild.values.forEach(visitor);
+ }
+
+ @override
+ void forgetChild(Element child) {
+ assert(slotToChild.containsValue(child));
+ assert(child.slot is _DecorationSlot);
+ assert(slotToChild.containsKey(child.slot));
+ slotToChild.remove(child.slot);
+ super.forgetChild(child);
+ }
+
+ void _mountChild(Widget? widget, _DecorationSlot slot) {
+ final Element? oldChild = slotToChild[slot];
+ final Element? newChild = updateChild(oldChild, widget, slot);
+ if (oldChild != null) {
+ slotToChild.remove(slot);
+ }
+ if (newChild != null) {
+ slotToChild[slot] = newChild;
+ }
+ }
+
+ @override
+ void mount(Element? parent, Object? newSlot) {
+ super.mount(parent, newSlot);
+ _mountChild(widget.decoration.icon, _DecorationSlot.icon);
+ _mountChild(widget.decoration.input, _DecorationSlot.input);
+ _mountChild(widget.decoration.label, _DecorationSlot.label);
+ _mountChild(widget.decoration.hint, _DecorationSlot.hint);
+ _mountChild(widget.decoration.prefix, _DecorationSlot.prefix);
+ _mountChild(widget.decoration.suffix, _DecorationSlot.suffix);
+ _mountChild(widget.decoration.prefixIcon, _DecorationSlot.prefixIcon);
+ _mountChild(widget.decoration.suffixIcon, _DecorationSlot.suffixIcon);
+ _mountChild(widget.decoration.helperError, _DecorationSlot.helperError);
+ _mountChild(widget.decoration.counter, _DecorationSlot.counter);
+ _mountChild(widget.decoration.container, _DecorationSlot.container);
+ }
+
+ void _updateChild(Widget? widget, _DecorationSlot slot) {
+ final Element? oldChild = slotToChild[slot];
+ final Element? newChild = updateChild(oldChild, widget, slot);
+ if (oldChild != null) {
+ slotToChild.remove(slot);
+ }
+ if (newChild != null) {
+ slotToChild[slot] = newChild;
+ }
+ }
+
+ @override
+ void update(_Decorator newWidget) {
+ super.update(newWidget);
+ assert(widget == newWidget);
+ _updateChild(widget.decoration.icon, _DecorationSlot.icon);
+ _updateChild(widget.decoration.input, _DecorationSlot.input);
+ _updateChild(widget.decoration.label, _DecorationSlot.label);
+ _updateChild(widget.decoration.hint, _DecorationSlot.hint);
+ _updateChild(widget.decoration.prefix, _DecorationSlot.prefix);
+ _updateChild(widget.decoration.suffix, _DecorationSlot.suffix);
+ _updateChild(widget.decoration.prefixIcon, _DecorationSlot.prefixIcon);
+ _updateChild(widget.decoration.suffixIcon, _DecorationSlot.suffixIcon);
+ _updateChild(widget.decoration.helperError, _DecorationSlot.helperError);
+ _updateChild(widget.decoration.counter, _DecorationSlot.counter);
+ _updateChild(widget.decoration.container, _DecorationSlot.container);
+ }
+
+ void _updateRenderObject(RenderBox? child, _DecorationSlot slot) {
+ switch (slot) {
+ case _DecorationSlot.icon:
+ renderObject.icon = child;
+ break;
+ case _DecorationSlot.input:
+ renderObject.input = child;
+ break;
+ case _DecorationSlot.label:
+ renderObject.label = child;
+ break;
+ case _DecorationSlot.hint:
+ renderObject.hint = child;
+ break;
+ case _DecorationSlot.prefix:
+ renderObject.prefix = child;
+ break;
+ case _DecorationSlot.suffix:
+ renderObject.suffix = child;
+ break;
+ case _DecorationSlot.prefixIcon:
+ renderObject.prefixIcon = child;
+ break;
+ case _DecorationSlot.suffixIcon:
+ renderObject.suffixIcon = child;
+ break;
+ case _DecorationSlot.helperError:
+ renderObject.helperError = child;
+ break;
+ case _DecorationSlot.counter:
+ renderObject.counter = child;
+ break;
+ case _DecorationSlot.container:
+ renderObject.container = child;
+ break;
+ }
+ }
+
+ @override
+ void insertRenderObjectChild(RenderObject child, _DecorationSlot slot) {
+ assert(child is RenderBox);
+ _updateRenderObject(child as RenderBox, slot);
+ assert(renderObject.children.keys.contains(slot));
+ }
+
+ @override
+ void removeRenderObjectChild(RenderObject child, _DecorationSlot slot) {
+ assert(child is RenderBox);
+ assert(renderObject.children[slot] == child);
+ _updateRenderObject(null, slot);
+ assert(!renderObject.children.keys.contains(slot));
+ }
+
+ @override
+ void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) {
+ assert(false, 'not reachable');
+ }
+}
+
+class _Decorator extends RenderObjectWidget {
const _Decorator({
Key? key,
required this.textAlignVertical,
@@ -1579,35 +1829,7 @@
final bool expands;
@override
- Iterable<_DecorationSlot> get slots => _DecorationSlot.values;
-
- @override
- Widget? childForSlot(_DecorationSlot slot) {
- switch (slot) {
- case _DecorationSlot.icon:
- return decoration.icon;
- case _DecorationSlot.input:
- return decoration.input;
- case _DecorationSlot.label:
- return decoration.label;
- case _DecorationSlot.hint:
- return decoration.hint;
- case _DecorationSlot.prefix:
- return decoration.prefix;
- case _DecorationSlot.suffix:
- return decoration.suffix;
- case _DecorationSlot.prefixIcon:
- return decoration.prefixIcon;
- case _DecorationSlot.suffixIcon:
- return decoration.suffixIcon;
- case _DecorationSlot.helperError:
- return decoration.helperError;
- case _DecorationSlot.counter:
- return decoration.counter;
- case _DecorationSlot.container:
- return decoration.container;
- }
- }
+ _DecorationElement createElement() => _DecorationElement(this);
@override
_RenderDecoration createRenderObject(BuildContext context) {
diff --git a/packages/flutter/lib/src/material/list_tile.dart b/packages/flutter/lib/src/material/list_tile.dart
index 87a3c11..1c68b55 100644
--- a/packages/flutter/lib/src/material/list_tile.dart
+++ b/packages/flutter/lib/src/material/list_tile.dart
@@ -1266,7 +1266,7 @@
trailing,
}
-class _ListTile extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<_ListTileSlot> {
+class _ListTile extends RenderObjectWidget {
const _ListTile({
Key? key,
this.leading,
@@ -1307,21 +1307,7 @@
final double minLeadingWidth;
@override
- Iterable<_ListTileSlot> get slots => _ListTileSlot.values;
-
- @override
- Widget? childForSlot(_ListTileSlot slot) {
- switch (slot) {
- case _ListTileSlot.leading:
- return leading;
- case _ListTileSlot.title:
- return title;
- case _ListTileSlot.subtitle:
- return subtitle;
- case _ListTileSlot.trailing:
- return trailing;
- }
- }
+ _ListTileElement createElement() => _ListTileElement(this);
@override
_RenderListTile createRenderObject(BuildContext context) {
@@ -1353,7 +1339,111 @@
}
}
-class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_ListTileSlot> {
+class _ListTileElement extends RenderObjectElement {
+ _ListTileElement(_ListTile widget) : super(widget);
+
+ final Map<_ListTileSlot, Element> slotToChild = <_ListTileSlot, Element>{};
+
+ @override
+ _ListTile get widget => super.widget as _ListTile;
+
+ @override
+ _RenderListTile get renderObject => super.renderObject as _RenderListTile;
+
+ @override
+ void visitChildren(ElementVisitor visitor) {
+ slotToChild.values.forEach(visitor);
+ }
+
+ @override
+ void forgetChild(Element child) {
+ assert(slotToChild.containsValue(child));
+ assert(child.slot is _ListTileSlot);
+ assert(slotToChild.containsKey(child.slot));
+ slotToChild.remove(child.slot);
+ super.forgetChild(child);
+ }
+
+ void _mountChild(Widget? widget, _ListTileSlot slot) {
+ final Element? oldChild = slotToChild[slot];
+ final Element? newChild = updateChild(oldChild, widget, slot);
+ if (oldChild != null) {
+ slotToChild.remove(slot);
+ }
+ if (newChild != null) {
+ slotToChild[slot] = newChild;
+ }
+ }
+
+ @override
+ void mount(Element? parent, Object? newSlot) {
+ super.mount(parent, newSlot);
+ _mountChild(widget.leading, _ListTileSlot.leading);
+ _mountChild(widget.title, _ListTileSlot.title);
+ _mountChild(widget.subtitle, _ListTileSlot.subtitle);
+ _mountChild(widget.trailing, _ListTileSlot.trailing);
+ }
+
+ void _updateChild(Widget? widget, _ListTileSlot slot) {
+ final Element? oldChild = slotToChild[slot];
+ final Element? newChild = updateChild(oldChild, widget, slot);
+ if (oldChild != null) {
+ slotToChild.remove(slot);
+ }
+ if (newChild != null) {
+ slotToChild[slot] = newChild;
+ }
+ }
+
+ @override
+ void update(_ListTile newWidget) {
+ super.update(newWidget);
+ assert(widget == newWidget);
+ _updateChild(widget.leading, _ListTileSlot.leading);
+ _updateChild(widget.title, _ListTileSlot.title);
+ _updateChild(widget.subtitle, _ListTileSlot.subtitle);
+ _updateChild(widget.trailing, _ListTileSlot.trailing);
+ }
+
+ void _updateRenderObject(RenderBox? child, _ListTileSlot slot) {
+ switch (slot) {
+ case _ListTileSlot.leading:
+ renderObject.leading = child;
+ break;
+ case _ListTileSlot.title:
+ renderObject.title = child;
+ break;
+ case _ListTileSlot.subtitle:
+ renderObject.subtitle = child;
+ break;
+ case _ListTileSlot.trailing:
+ renderObject.trailing = child;
+ break;
+ }
+ }
+
+ @override
+ void insertRenderObjectChild(RenderObject child, _ListTileSlot slot) {
+ assert(child is RenderBox);
+ _updateRenderObject(child as RenderBox, slot);
+ assert(renderObject.children.keys.contains(slot));
+ }
+
+ @override
+ void removeRenderObjectChild(RenderObject child, _ListTileSlot slot) {
+ assert(child is RenderBox);
+ assert(renderObject.children[slot] == child);
+ _updateRenderObject(null, slot);
+ assert(!renderObject.children.keys.contains(slot));
+ }
+
+ @override
+ void moveRenderObjectChild(RenderObject child, Object? oldSlot, Object? newSlot) {
+ assert(false, 'not reachable');
+ }
+}
+
+class _RenderListTile extends RenderBox {
_RenderListTile({
required bool isDense,
required VisualDensity visualDensity,
@@ -1382,14 +1472,46 @@
_minVerticalPadding = minVerticalPadding,
_minLeadingWidth = minLeadingWidth;
- RenderBox? get leading => childForSlot(_ListTileSlot.leading);
- RenderBox? get title => childForSlot(_ListTileSlot.title);
- RenderBox? get subtitle => childForSlot(_ListTileSlot.subtitle);
- RenderBox? get trailing => childForSlot(_ListTileSlot.trailing);
+ final Map<_ListTileSlot, RenderBox> children = <_ListTileSlot, RenderBox>{};
+
+ RenderBox? _updateChild(RenderBox? oldChild, RenderBox? newChild, _ListTileSlot slot) {
+ if (oldChild != null) {
+ dropChild(oldChild);
+ children.remove(slot);
+ }
+ if (newChild != null) {
+ children[slot] = newChild;
+ adoptChild(newChild);
+ }
+ return newChild;
+ }
+
+ RenderBox? _leading;
+ RenderBox? get leading => _leading;
+ set leading(RenderBox? value) {
+ _leading = _updateChild(_leading, value, _ListTileSlot.leading);
+ }
+
+ RenderBox? _title;
+ RenderBox? get title => _title;
+ set title(RenderBox? value) {
+ _title = _updateChild(_title, value, _ListTileSlot.title);
+ }
+
+ RenderBox? _subtitle;
+ RenderBox? get subtitle => _subtitle;
+ set subtitle(RenderBox? value) {
+ _subtitle = _updateChild(_subtitle, value, _ListTileSlot.subtitle);
+ }
+
+ RenderBox? _trailing;
+ RenderBox? get trailing => _trailing;
+ set trailing(RenderBox? value) {
+ _trailing = _updateChild(_trailing, value, _ListTileSlot.trailing);
+ }
// The returned list is ordered for hit testing.
- @override
- Iterable<RenderBox> get children sync* {
+ Iterable<RenderBox> get _children sync* {
if (leading != null)
yield leading!;
if (title != null)
@@ -1494,6 +1616,44 @@
}
@override
+ void attach(PipelineOwner owner) {
+ super.attach(owner);
+ for (final RenderBox child in _children)
+ child.attach(owner);
+ }
+
+ @override
+ void detach() {
+ super.detach();
+ for (final RenderBox child in _children)
+ child.detach();
+ }
+
+ @override
+ void redepthChildren() {
+ _children.forEach(redepthChild);
+ }
+
+ @override
+ void visitChildren(RenderObjectVisitor visitor) {
+ _children.forEach(visitor);
+ }
+
+ @override
+ List<DiagnosticsNode> debugDescribeChildren() {
+ final List<DiagnosticsNode> value = <DiagnosticsNode>[];
+ void add(RenderBox? child, String name) {
+ if (child != null)
+ value.add(child.toDiagnosticsNode(name: name));
+ }
+ add(leading, 'leading');
+ add(title, 'title');
+ add(subtitle, 'subtitle');
+ add(trailing, 'trailing');
+ return value;
+ }
+
+ @override
bool get sizedByParent => false;
static double _minWidth(RenderBox? box, double height) {
@@ -1745,7 +1905,7 @@
@override
bool hitTestChildren(BoxHitTestResult result, { required Offset position }) {
assert(position != null);
- for (final RenderBox child in children) {
+ for (final RenderBox child in _children) {
final BoxParentData parentData = child.parentData! as BoxParentData;
final bool isHit = result.addWithPaintOffset(
offset: parentData.offset,
diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart
index cf95c13..dd4248f 100644
--- a/packages/flutter/lib/src/rendering/object.dart
+++ b/packages/flutter/lib/src/rendering/object.dart
@@ -3167,11 +3167,6 @@
/// parent data.
///
/// Moreover, this is a required mixin for render objects returned to [MultiChildRenderObjectWidget].
-///
-/// See also:
-///
-/// * [SlottedContainerRenderObjectMixin], which organizes its children
-/// in different named slots.
mixin ContainerRenderObjectMixin<ChildType extends RenderObject, ParentDataType extends ContainerParentDataMixin<ChildType>> on RenderObject {
bool _debugUltimatePreviousSiblingOf(ChildType child, { ChildType? equals }) {
ParentDataType childParentData = child.parentData! as ParentDataType;
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart
index 31cf0d0..ed0424f 100644
--- a/packages/flutter/lib/src/widgets/framework.dart
+++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -1677,13 +1677,6 @@
/// RenderObjectWidgets provide the configuration for [RenderObjectElement]s,
/// which wrap [RenderObject]s, which provide the actual rendering of the
/// application.
-///
-/// See also:
-///
-/// * [MultiChildRenderObjectWidget], which configures a [RenderObject] with
-/// a single list of children.
-/// * [SlottedMultiChildRenderObjectWidgetMixin], which configures a
-/// [RenderObject] that organizes its children in different named slots.
abstract class RenderObjectWidget extends Widget {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
@@ -1774,11 +1767,7 @@
/// See also:
///
/// * [Stack], which uses [MultiChildRenderObjectWidget].
-/// * [RenderStack], for an example implementation of the associated render
-/// object.
-/// * [SlottedMultiChildRenderObjectWidgetMixin], which configures a
-/// [RenderObject] that instead of having a single list of children organizes
-/// its children in named slots.
+/// * [RenderStack], for an example implementation of the associated render object.
abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {
/// Initializes fields for subclasses.
///
diff --git a/packages/flutter/lib/src/widgets/slotted_render_object_widget.dart b/packages/flutter/lib/src/widgets/slotted_render_object_widget.dart
deleted file mode 100644
index 25ae7b4..0000000
--- a/packages/flutter/lib/src/widgets/slotted_render_object_widget.dart
+++ /dev/null
@@ -1,271 +0,0 @@
-// 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 mixin for a [RenderObjectWidget] that configures a [RenderObject]
-/// subclass, which organizes its children in different slots.
-///
-/// Implementers of this mixin have to provide the list of available slots by
-/// overriding [slots]. The list of slots must never change for a given class
-/// implementing this mixin. In the common case, [Enum] values are used as slots
-/// and [slots] is typically implemented to return the value of the enum's
-/// `values` getter.
-///
-/// Furthermore, [childForSlot] must be implemented to return the current
-/// widget configuration for a given slot.
-///
-/// The [RenderObject] returned by [createRenderObject] and updated by
-/// [updateRenderObject] must implement the [SlottedContainerRenderObjectMixin].
-///
-/// The type parameter `S` is the type for the slots to be used by this
-/// [RenderObjectWidget] and the [RenderObject] it configures. In the typical
-/// case, `S` is an [Enum] type.
-///
-/// {@tool dartpad}
-/// This example uses the [SlottedMultiChildRenderObjectWidgetMixin] in
-/// combination with the [SlottedContainerRenderObjectMixin] to implement a
-/// widget that provides two slots: topLeft and bottomRight. The widget arranges
-/// the children in those slots diagonally.
-///
-/// ** See code in examples/api/lib/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart **
-/// {@end-tool}
-///
-/// See also:
-///
-/// * [MultiChildRenderObjectWidget], which configures a [RenderObject]
-/// with a single list of children.
-/// * [ListTile], which uses [SlottedMultiChildRenderObjectWidgetMixin] in its
-/// internal (private) implementation.
-mixin SlottedMultiChildRenderObjectWidgetMixin<S> on RenderObjectWidget {
- /// Returns a list of all available slots.
- ///
- /// The list of slots must be static and must never change for a given class
- /// implementing this mixin.
- ///
- /// Typically, an [Enum] is used to identify the different slots. In that case
- /// this getter can be implemented by returning what the `values` getter
- /// of the enum used returns.
- @protected
- Iterable<S> get slots;
-
- /// Returns the widget that is currently occupying the provided `slot`.
- ///
- /// The [RenderObject] configured by this class will be configured to have
- /// the [RenderObject] produced by the returned [Widget] in the provided
- /// `slot`.
- @protected
- Widget? childForSlot(S slot);
-
- @override
- SlottedContainerRenderObjectMixin<S> createRenderObject(BuildContext context);
-
- @override
- void updateRenderObject(BuildContext context, SlottedContainerRenderObjectMixin<S> renderObject);
-
- @override
- SlottedRenderObjectElement<S> createElement() => SlottedRenderObjectElement<S>(this);
-}
-
-/// Mixin for a [RenderBox] configured by a [SlottedMultiChildRenderObjectWidgetMixin].
-///
-/// The [RenderBox] child currently occupying a given slot can be obtained by
-/// calling [childForSlot].
-///
-/// Implementers may consider overriding [children] to return the children
-/// of this render object in a consistent order (e.g. hit test order).
-///
-/// The type parameter `S` is the type for the slots to be used by this
-/// [RenderObject] and the [SlottedMultiChildRenderObjectWidgetMixin] it was
-/// configured by. In the typical case, `S` is an [Enum] type.
-///
-/// See [SlottedMultiChildRenderObjectWidgetMixin] for example code showcasing
-/// how this mixin is used in combination with the
-/// [SlottedMultiChildRenderObjectWidgetMixin].
-///
-/// See also:
-///
-/// * [ContainerRenderObjectMixin], which organizes its children in a single
-/// list.
-mixin SlottedContainerRenderObjectMixin<S> on RenderBox {
- /// Returns the [RenderBox] child that is currently occupying the provided
- /// `slot`.
- ///
- /// Returns null if no [RenderBox] is configured for the given slot.
- @protected
- RenderBox? childForSlot(S slot) => _slotToChild[slot];
-
- /// Returns an [Iterable] of all non-null children.
- ///
- /// This getter is used by the default implementation of [attach], [detach],
- /// [redepthChildren], [visitChildren], and [debugDescribeChildren] to iterate
- /// over the children of this [RenderBox]. The base implementation makes no
- /// guarantee about the order in which the children are returned. Subclasses,
- /// for which the child order is important should override this getter and
- /// return the children in the desired order.
- @protected
- Iterable<RenderBox> get children => _slotToChild.values;
-
- /// Returns the debug name for a given `slot`.
- ///
- /// This method is called by [debugDescribeChildren] for each slot that is
- /// currently occupied by a child to obtain a name for that slot for debug
- /// outputs.
- ///
- /// The default implementation calls [EnumName.name] on `slot` it it is an
- /// [Enum] value and `toString` if it is not.
- @protected
- String debugNameForSlot(S slot) {
- if (slot is Enum) {
- return slot.name;
- }
- return slot.toString();
- }
-
- @override
- void attach(PipelineOwner owner) {
- super.attach(owner);
- for (final RenderBox child in children) {
- child.attach(owner);
- }
- }
-
- @override
- void detach() {
- super.detach();
- for (final RenderBox child in children) {
- child.detach();
- }
- }
-
- @override
- void redepthChildren() {
- children.forEach(redepthChild);
- }
-
- @override
- void visitChildren(RenderObjectVisitor visitor) {
- children.forEach(visitor);
- }
-
- @override
- List<DiagnosticsNode> debugDescribeChildren() {
- final List<DiagnosticsNode> value = <DiagnosticsNode>[];
- final Map<RenderBox, S> childToSlot = Map<RenderBox, S>.fromIterables(
- _slotToChild.values,
- _slotToChild.keys,
- );
- for (final RenderBox child in children) {
- _addDiagnostics(child, value, debugNameForSlot(childToSlot[child] as S));
- }
- return value;
- }
-
- void _addDiagnostics(RenderBox child, List<DiagnosticsNode> value, String name) {
- value.add(child.toDiagnosticsNode(name: name));
- }
-
- final Map<S, RenderBox> _slotToChild = <S, RenderBox>{};
-
- void _setChild(RenderBox? child, S slot) {
- final RenderBox? oldChild = _slotToChild[slot];
- if (oldChild != null) {
- dropChild(oldChild);
- _slotToChild.remove(slot);
- }
- if (child != null) {
- _slotToChild[slot] = child;
- adoptChild(child);
- }
- }
-}
-
-/// Element used by the [SlottedMultiChildRenderObjectWidgetMixin].
-class SlottedRenderObjectElement<S> extends RenderObjectElement {
- /// Creates an element that uses the given widget as its configuration.
- SlottedRenderObjectElement(SlottedMultiChildRenderObjectWidgetMixin<S> widget) : super(widget);
-
- final Map<S, Element> _slotToChild = <S, Element>{};
-
- @override
- SlottedMultiChildRenderObjectWidgetMixin<S> get widget => super.widget as SlottedMultiChildRenderObjectWidgetMixin<S>;
-
- @override
- SlottedContainerRenderObjectMixin<S> get renderObject => super.renderObject as SlottedContainerRenderObjectMixin<S>;
-
- @override
- void visitChildren(ElementVisitor visitor) {
- _slotToChild.values.forEach(visitor);
- }
-
- @override
- void forgetChild(Element child) {
- assert(_slotToChild.containsValue(child));
- assert(child.slot is S);
- assert(_slotToChild.containsKey(child.slot));
- _slotToChild.remove(child.slot);
- super.forgetChild(child);
- }
-
- @override
- void mount(Element? parent, Object? newSlot) {
- super.mount(parent, newSlot);
- _updateChildren();
- }
-
- @override
- void update(SlottedMultiChildRenderObjectWidgetMixin<S> newWidget) {
- super.update(newWidget);
- assert(widget == newWidget);
- _updateChildren();
- }
-
- List<S>? _debugPreviousSlots;
-
- void _updateChildren() {
- assert(() {
- _debugPreviousSlots ??= widget.slots.toList();
- return listEquals(_debugPreviousSlots, widget.slots.toList());
- }(), '${widget.runtimeType}.slots must not change.');
- assert(widget.slots.toSet().length == widget.slots.length, 'slots must be unique');
-
- for (final S slot in widget.slots) {
- _updateChild(widget.childForSlot(slot), slot);
- }
- }
-
- void _updateChild(Widget? widget, S slot) {
- final Element? oldChild = _slotToChild[slot];
- assert(oldChild == null || oldChild.slot == slot); // Reason why [moveRenderObjectChild] is not reachable.
- final Element? newChild = updateChild(oldChild, widget, slot);
- if (oldChild != null) {
- _slotToChild.remove(slot);
- }
- if (newChild != null) {
- _slotToChild[slot] = newChild;
- }
- }
-
- @override
- void insertRenderObjectChild(RenderBox child, S slot) {
- renderObject._setChild(child, slot);
- assert(renderObject._slotToChild[slot] == child);
- }
-
- @override
- void removeRenderObjectChild(RenderBox child, S slot) {
- assert(renderObject._slotToChild[slot] == child);
- renderObject._setChild(null, slot);
- assert(renderObject._slotToChild[slot] == null);
- }
-
- @override
- void moveRenderObjectChild(RenderBox child, Object? oldSlot, Object? newSlot) {
- // Existing elements are never moved to a new slot, see assert in [_updateChild].
- assert(false, 'not reachable');
- }
-}
diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart
index 62e702e..c424d6e 100644
--- a/packages/flutter/lib/widgets.dart
+++ b/packages/flutter/lib/widgets.dart
@@ -116,7 +116,6 @@
export 'src/widgets/sliver_layout_builder.dart';
export 'src/widgets/sliver_persistent_header.dart';
export 'src/widgets/sliver_prototype_extent_list.dart';
-export 'src/widgets/slotted_render_object_widget.dart';
export 'src/widgets/spacer.dart';
export 'src/widgets/status_transitions.dart';
export 'src/widgets/table.dart';
diff --git a/packages/flutter/test/material/chip_test.dart b/packages/flutter/test/material/chip_test.dart
index e229d00..f4e0a7c 100644
--- a/packages/flutter/test/material/chip_test.dart
+++ b/packages/flutter/test/material/chip_test.dart
@@ -15,7 +15,7 @@
import 'feedback_tester.dart';
Finder findRenderChipElement() {
- return find.byElementPredicate((Element e) => '${e.renderObject.runtimeType}' == '_RenderChip');
+ return find.byElementPredicate((Element e) => '${e.runtimeType}' == '_RenderChipElement');
}
RenderBox getMaterialBox(WidgetTester tester) {
diff --git a/packages/flutter/test/widgets/slotted_render_object_widget_test.dart b/packages/flutter/test/widgets/slotted_render_object_widget_test.dart
deleted file mode 100644
index 7da4ae4..0000000
--- a/packages/flutter/test/widgets/slotted_render_object_widget_test.dart
+++ /dev/null
@@ -1,292 +0,0 @@
-// 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/material.dart';
-import 'package:flutter/rendering.dart';
-import 'package:flutter/widgets.dart';
-import 'package:flutter_test/flutter_test.dart';
-
-import '../rendering/mock_canvas.dart';
-
-const Color green = Color(0xFF00FF00);
-const Color yellow = Color(0xFFFFFF00);
-
-void main() {
- testWidgets('SlottedRenderObjectWidget test', (WidgetTester tester) async {
- await tester.pumpWidget(buildWidget(
- topLeft: Container(
- height: 100,
- width: 80,
- color: yellow,
- child: const Text('topLeft'),
- ),
- bottomRight: Container(
- height: 120,
- width: 110,
- color: green,
- child: const Text('bottomRight'),
- ),
- ));
-
- expect(find.text('topLeft'), findsOneWidget);
- expect(find.text('bottomRight'), findsOneWidget);
- expect(tester.getSize(find.byType(_Diagonal)), const Size(80 + 110, 100 + 120));
- expect(find.byType(_Diagonal), paints
- ..rect(
- rect: const Rect.fromLTWH(0, 0, 80, 100),
- color: yellow,
- )
- ..rect(
- rect: const Rect.fromLTWH(80, 100, 110, 120),
- color: green,
- )
- );
-
- await tester.pumpWidget(buildWidget(
- topLeft: Container(
- height: 200,
- width: 100,
- color: yellow,
- child: const Text('topLeft'),
- ),
- bottomRight: Container(
- height: 220,
- width: 210,
- color: green,
- child: const Text('bottomRight'),
- ),
- ));
-
- expect(find.text('topLeft'), findsOneWidget);
- expect(find.text('bottomRight'), findsOneWidget);
- expect(tester.getSize(find.byType(_Diagonal)), const Size(100 + 210, 200 + 220));
- expect(find.byType(_Diagonal), paints
- ..rect(
- rect: const Rect.fromLTWH(0, 0, 100, 200),
- color: yellow,
- )
- ..rect(
- rect: const Rect.fromLTWH(100, 200, 210, 220),
- color: green,
- )
- );
-
- await tester.pumpWidget(buildWidget(
- topLeft: Container(
- height: 200,
- width: 100,
- color: yellow,
- child: const Text('topLeft'),
- ),
- bottomRight: Container(
- key: UniqueKey(),
- height: 230,
- width: 220,
- color: green,
- child: const Text('bottomRight'),
- ),
- ));
-
- expect(find.text('topLeft'), findsOneWidget);
- expect(find.text('bottomRight'), findsOneWidget);
- expect(tester.getSize(find.byType(_Diagonal)), const Size(100 + 220, 200 + 230));
- expect(find.byType(_Diagonal), paints
- ..rect(
- rect: const Rect.fromLTWH(0, 0, 100, 200),
- color: yellow,
- )
- ..rect(
- rect: const Rect.fromLTWH(100, 200, 220, 230),
- color: green,
- )
- );
-
- await tester.pumpWidget(buildWidget(
- topLeft: Container(
- height: 200,
- width: 100,
- color: yellow,
- child: const Text('topLeft'),
- ),
- ));
-
- expect(find.text('topLeft'), findsOneWidget);
- expect(find.text('bottomRight'), findsNothing);
- expect(tester.getSize(find.byType(_Diagonal)), const Size(100, 200));
- expect(find.byType(_Diagonal), paints
- ..rect(
- rect: const Rect.fromLTWH(0, 0, 100, 200),
- color: yellow,
- )
- );
-
- await tester.pumpWidget(buildWidget());
- expect(find.text('topLeft'), findsNothing);
- expect(find.text('bottomRight'), findsNothing);
- expect(tester.getSize(find.byType(_Diagonal)), Size.zero);
- expect(find.byType(_Diagonal), paintsNothing);
-
- await tester.pumpWidget(Container());
- expect(find.byType(_Diagonal), findsNothing);
- });
-
- test('nameForSlot', () {
- expect(_RenderDiagonal().publicNameForSlot(_DiagonalSlot.bottomRight), 'bottomRight');
- expect(_RenderDiagonal().publicNameForSlot(_DiagonalSlot.topLeft), 'topLeft');
- final _Slot slot = _Slot();
- expect(_RenderTest().publicNameForSlot(slot), slot.toString());
- });
-
- testWidgets('debugDescribeChildren', (WidgetTester tester) async {
- await tester.pumpWidget(buildWidget(
- topLeft: const SizedBox(
- height: 100,
- width: 80,
- ),
- bottomRight: const SizedBox(
- height: 120,
- width: 110,
- ),
- ));
-
- expect(
- tester.renderObject(find.byType(_Diagonal)).toStringDeep(),
- equalsIgnoringHashCodes(r'''
-_RenderDiagonal#00000 relayoutBoundary=up1
- │ creator: _Diagonal ← Align ← Directionality ← [root]
- │ parentData: offset=Offset(0.0, 0.0) (can use size)
- │ constraints: BoxConstraints(0.0<=w<=800.0, 0.0<=h<=600.0)
- │ size: Size(190.0, 220.0)
- │
- ├─topLeft: RenderConstrainedBox#00000 relayoutBoundary=up2
- │ creator: SizedBox ← _Diagonal ← Align ← Directionality ← [root]
- │ parentData: offset=Offset(0.0, 0.0) (can use size)
- │ constraints: BoxConstraints(unconstrained)
- │ size: Size(80.0, 100.0)
- │ additionalConstraints: BoxConstraints(w=80.0, h=100.0)
- │
- └─bottomRight: RenderConstrainedBox#00000 relayoutBoundary=up2
- creator: SizedBox ← _Diagonal ← Align ← Directionality ← [root]
- parentData: offset=Offset(80.0, 100.0) (can use size)
- constraints: BoxConstraints(unconstrained)
- size: Size(110.0, 120.0)
- additionalConstraints: BoxConstraints(w=110.0, h=120.0)
-''')
- );
- });
-}
-
-Widget buildWidget({Widget? topLeft, Widget? bottomRight}) {
- return Directionality(
- textDirection: TextDirection.ltr,
- child: Align(
- alignment: Alignment.topLeft,
- child: _Diagonal(
- topLeft: topLeft,
- bottomRight: bottomRight,
- ),
- ),
- );
-}
-
-enum _DiagonalSlot {
- topLeft,
- bottomRight,
-}
-
-class _Diagonal extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<_DiagonalSlot> {
- const _Diagonal({
- Key? key,
- this.topLeft,
- this.bottomRight,
- this.backgroundColor,
- }) : super(key: key);
-
- final Widget? topLeft;
- final Widget? bottomRight;
- final Color? backgroundColor;
-
- @override
- Iterable<_DiagonalSlot> get slots => _DiagonalSlot.values;
-
- @override
- Widget? childForSlot(Object slot) {
- switch (slot) {
- case _DiagonalSlot.topLeft:
- return topLeft;
- case _DiagonalSlot.bottomRight:
- return bottomRight;
- }
- }
-
- @override
- SlottedContainerRenderObjectMixin<_DiagonalSlot> createRenderObject(
- BuildContext context,
- ) {
- return _RenderDiagonal();
- }
-}
-
-class _RenderDiagonal extends RenderBox with SlottedContainerRenderObjectMixin<_DiagonalSlot> {
- RenderBox? get _topLeft => childForSlot(_DiagonalSlot.topLeft);
- RenderBox? get _bottomRight => childForSlot(_DiagonalSlot.bottomRight);
-
- @override
- void performLayout() {
- const BoxConstraints childConstraints = BoxConstraints();
-
- Size topLeftSize = Size.zero;
- if (_topLeft != null) {
- _topLeft!.layout(childConstraints, parentUsesSize: true);
- _positionChild(_topLeft!, Offset.zero);
- topLeftSize = _topLeft!.size;
- }
-
- Size bottomRightSize = Size.zero;
- if (_bottomRight != null) {
- _bottomRight!.layout(childConstraints, parentUsesSize: true);
- _positionChild(
- _bottomRight!,
- Offset(topLeftSize.width, topLeftSize.height),
- );
- bottomRightSize = _bottomRight!.size;
- }
-
- size = constraints.constrain(Size(
- topLeftSize.width + bottomRightSize.width,
- topLeftSize.height + bottomRightSize.height,
- ));
- }
-
- void _positionChild(RenderBox child, Offset offset) {
- (child.parentData! as BoxParentData).offset = offset;
- }
-
- @override
- void paint(PaintingContext context, Offset offset) {
- if (_topLeft != null) {
- _paintChild(_topLeft!, context, offset);
- }
- if (_bottomRight != null) {
- _paintChild(_bottomRight!, context, offset);
- }
- }
-
- void _paintChild(RenderBox child, PaintingContext context, Offset offset) {
- final BoxParentData childParentData = child.parentData! as BoxParentData;
- context.paintChild(child, childParentData.offset + offset);
- }
-
- String publicNameForSlot(_DiagonalSlot slot) => debugNameForSlot(slot);
-}
-
-class _Slot {
- @override
- String toString() => describeIdentity(this);
-}
-
-class _RenderTest extends RenderBox with SlottedContainerRenderObjectMixin<_Slot> {
- String publicNameForSlot(_Slot slot) => debugNameForSlot(slot);
-}