Make SlottedMultiChildRenderObjectWidgetMixin a concrete class (#126108) This is a proof of concept for renaming SlottedMultiChildRenderObjectWidgetMixin to SlottedMultiChildRenderObjectWidget and making it a concrete class. I also made SlottedContainerRenderObjectMixin generic instead of being specialized to RenderBox. I don't think this is something we can easily automigrate, but we may not need to, I don't know how common this is...
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 index e93e5f9..7105802 100644 --- 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
@@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -/// Flutter code sample for [SlottedMultiChildRenderObjectWidgetMixin]. +/// Flutter code sample for [SlottedMultiChildRenderObjectWidget]. /// Slots used for the children of [Diagonal] and [RenderDiagonal]. enum DiagonalSlot { @@ -14,9 +14,9 @@ } /// A widget that demonstrates the usage of -/// [SlottedMultiChildRenderObjectWidgetMixin] by providing slots for two +/// [SlottedMultiChildRenderObjectWidget] by providing slots for two /// children that will be arranged diagonally. -class Diagonal extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<DiagonalSlot> { +class Diagonal extends SlottedMultiChildRenderObjectWidget<DiagonalSlot, RenderBox> { const Diagonal({ super.key, this.topLeft, @@ -49,7 +49,7 @@ // [SlottedRenderObjectElement.update]. @override - SlottedContainerRenderObjectMixin<DiagonalSlot> createRenderObject( + SlottedContainerRenderObjectMixin<DiagonalSlot, RenderBox> createRenderObject( BuildContext context, ) { return RenderDiagonal( @@ -60,7 +60,7 @@ @override void updateRenderObject( BuildContext context, - SlottedContainerRenderObjectMixin<DiagonalSlot> renderObject, + SlottedContainerRenderObjectMixin<DiagonalSlot, RenderBox> renderObject, ) { (renderObject as RenderDiagonal).backgroundColor = backgroundColor; } @@ -70,7 +70,7 @@ /// [SlottedContainerRenderObjectMixin] by providing slots for two children that /// will be arranged diagonally. class RenderDiagonal extends RenderBox - with SlottedContainerRenderObjectMixin<DiagonalSlot>, DebugOverflowIndicatorMixin { + with SlottedContainerRenderObjectMixin<DiagonalSlot, RenderBox>, DebugOverflowIndicatorMixin { RenderDiagonal({Color? backgroundColor}) : _backgroundColor = backgroundColor; // Getters and setters to configure the [RenderObject] with the configuration
diff --git a/packages/flutter/lib/src/material/chip.dart b/packages/flutter/lib/src/material/chip.dart index e794ef3..f6b3510 100644 --- a/packages/flutter/lib/src/material/chip.dart +++ b/packages/flutter/lib/src/material/chip.dart
@@ -1342,7 +1342,7 @@ } } -class _ChipRenderWidget extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<_ChipSlot> { +class _ChipRenderWidget extends SlottedMultiChildRenderObjectWidget<_ChipSlot, RenderBox> { const _ChipRenderWidget({ required this.theme, this.value, @@ -1393,7 +1393,7 @@ } @override - SlottedContainerRenderObjectMixin<_ChipSlot> createRenderObject(BuildContext context) { + SlottedContainerRenderObjectMixin<_ChipSlot, RenderBox> createRenderObject(BuildContext context) { return _RenderChip( theme: theme, textDirection: Directionality.of(context), @@ -1478,7 +1478,7 @@ ); } -class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_ChipSlot> { +class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_ChipSlot, RenderBox> { _RenderChip({ required _ChipRenderTheme theme, required TextDirection textDirection,
diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index 39f0185..18f8ffd 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart
@@ -693,7 +693,7 @@ } // The workhorse: layout and paint a _Decorator widget's _Decoration. -class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin<_DecorationSlot> { +class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin<_DecorationSlot, RenderBox> { _RenderDecoration({ required _Decoration decoration, required TextDirection textDirection, @@ -1637,7 +1637,7 @@ } } -class _Decorator extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<_DecorationSlot> { +class _Decorator extends SlottedMultiChildRenderObjectWidget<_DecorationSlot, RenderBox> { const _Decorator({ required this.textAlignVertical, required this.decoration,
diff --git a/packages/flutter/lib/src/material/list_tile.dart b/packages/flutter/lib/src/material/list_tile.dart index 1b0abbe..e96ea52 100644 --- a/packages/flutter/lib/src/material/list_tile.dart +++ b/packages/flutter/lib/src/material/list_tile.dart
@@ -967,7 +967,7 @@ trailing, } -class _ListTile extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<_ListTileSlot> { +class _ListTile extends SlottedMultiChildRenderObjectWidget<_ListTileSlot, RenderBox> { const _ListTile({ this.leading, required this.title, @@ -1049,7 +1049,7 @@ } } -class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_ListTileSlot> { +class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_ListTileSlot, RenderBox> { _RenderListTile({ required bool isDense, required VisualDensity visualDensity,
diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index 92874bd..50eb6ca 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart
@@ -1760,7 +1760,7 @@ /// /// * [MultiChildRenderObjectWidget], which configures a [RenderObject] with /// a single list of children. -/// * [SlottedMultiChildRenderObjectWidgetMixin], which configures a +/// * [SlottedMultiChildRenderObjectWidget], 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 @@ -1854,7 +1854,7 @@ /// * [Stack], which uses [MultiChildRenderObjectWidget]. /// * [RenderStack], for an example implementation of the associated render /// object. -/// * [SlottedMultiChildRenderObjectWidgetMixin], which configures a +/// * [SlottedMultiChildRenderObjectWidget], which configures a /// [RenderObject] that instead of having a single list of children organizes /// its children in named slots. abstract class MultiChildRenderObjectWidget extends RenderObjectWidget {
diff --git a/packages/flutter/lib/src/widgets/slotted_render_object_widget.dart b/packages/flutter/lib/src/widgets/slotted_render_object_widget.dart index d3bd339..1318c04 100644 --- a/packages/flutter/lib/src/widgets/slotted_render_object_widget.dart +++ b/packages/flutter/lib/src/widgets/slotted_render_object_widget.dart
@@ -7,8 +7,8 @@ import 'framework.dart'; -/// A mixin for a [RenderObjectWidget] that configures a [RenderObject] -/// subclass, which organizes its children in different slots. +/// A superclass for [RenderObjectWidget]s that configure [RenderObject] +/// subclasses that organize their 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 @@ -20,14 +20,19 @@ /// widget configuration for a given slot. /// /// The [RenderObject] returned by [createRenderObject] and updated by -/// [updateRenderObject] must implement the [SlottedContainerRenderObjectMixin]. +/// [updateRenderObject] must implement [SlottedContainerRenderObjectMixin]. /// -/// The type parameter `S` is the type for the slots to be used by this +/// The type parameter `SlotType` 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. +/// case, `SlotType` is an [Enum] type. +/// +/// The type parameter `ChildType` is the type used for the [RenderObject] children +/// (e.g. [RenderBox] or [RenderSliver]). In the typical case, `ChildType` is +/// [RenderBox]. This class does not support having different kinds of children +/// for different slots. /// /// {@tool dartpad} -/// This example uses the [SlottedMultiChildRenderObjectWidgetMixin] in +/// This example uses the [SlottedMultiChildRenderObjectWidget] 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. @@ -39,9 +44,25 @@ /// /// * [MultiChildRenderObjectWidget], which configures a [RenderObject] /// with a single list of children. -/// * [ListTile], which uses [SlottedMultiChildRenderObjectWidgetMixin] in its +/// * [ListTile], which uses [SlottedMultiChildRenderObjectWidget] in its /// internal (private) implementation. -mixin SlottedMultiChildRenderObjectWidgetMixin<S> on RenderObjectWidget { +abstract class SlottedMultiChildRenderObjectWidget<SlotType, ChildType extends RenderObject> extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<SlotType, ChildType> { + /// Abstract const constructor. This constructor enables subclasses to provide + /// const constructors so that they can be used in const expressions. + const SlottedMultiChildRenderObjectWidget({ super.key }); +} + +/// A mixin version of [SlottedMultiChildRenderObjectWidget]. +/// +/// This mixin provides the same logic as extending +/// [SlottedMultiChildRenderObjectWidget] directly. +/// +/// It was deprecated to simplify the process of creating slotted widgets. +@Deprecated( + 'Extend SlottedMultiChildRenderObjectWidget instead of mixing in SlottedMultiChildRenderObjectWidgetMixin. ' + 'This feature was deprecated after v3.10.0-1.5.pre.' +) +mixin SlottedMultiChildRenderObjectWidgetMixin<SlotType, ChildType extends RenderObject> on RenderObjectWidget { /// Returns a list of all available slots. /// /// The list of slots must be static and must never change for a given class @@ -51,7 +72,7 @@ /// this getter can be implemented by returning what the `values` getter /// of the enum used returns. @protected - Iterable<S> get slots; + Iterable<SlotType> get slots; /// Returns the widget that is currently occupying the provided `slot`. /// @@ -59,56 +80,60 @@ /// the [RenderObject] produced by the returned [Widget] in the provided /// `slot`. @protected - Widget? childForSlot(S slot); + Widget? childForSlot(SlotType slot); @override - SlottedContainerRenderObjectMixin<S> createRenderObject(BuildContext context); + SlottedContainerRenderObjectMixin<SlotType, ChildType> createRenderObject(BuildContext context); @override - void updateRenderObject(BuildContext context, SlottedContainerRenderObjectMixin<S> renderObject); + void updateRenderObject(BuildContext context, SlottedContainerRenderObjectMixin<SlotType, ChildType> renderObject); @override - SlottedRenderObjectElement<S> createElement() => SlottedRenderObjectElement<S>(this); + SlottedRenderObjectElement<SlotType, ChildType> createElement() => SlottedRenderObjectElement<SlotType, ChildType>(this); } -/// Mixin for a [RenderBox] configured by a [SlottedMultiChildRenderObjectWidgetMixin]. +/// Mixin for a [RenderObject] configured by a [SlottedMultiChildRenderObjectWidget]. /// -/// The [RenderBox] child currently occupying a given slot can be obtained by +/// The [RenderObject] 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. +/// The type parameter `SlotType` is the type for the slots to be used by this +/// [RenderObject] and the [SlottedMultiChildRenderObjectWidget] it was +/// configured by. In the typical case, `SlotType` is an [Enum] type. /// -/// See [SlottedMultiChildRenderObjectWidgetMixin] for example code showcasing -/// how this mixin is used in combination with the -/// [SlottedMultiChildRenderObjectWidgetMixin]. +/// The type parameter `ChildType` is the type of [RenderObject] used for the children +/// (e.g. [RenderBox] or [RenderSliver]). In the typical case, `ChildType` is +/// [RenderBox]. This mixin does not support having different kinds of children +/// for different slots. +/// +/// See [SlottedMultiChildRenderObjectWidget] for example code showcasing how +/// this mixin is used in combination with [SlottedMultiChildRenderObjectWidget]. /// /// 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 +mixin SlottedContainerRenderObjectMixin<SlotType, ChildType extends RenderObject> on RenderObject { + /// Returns the [RenderObject] child that is currently occupying the provided /// `slot`. /// - /// Returns null if no [RenderBox] is configured for the given slot. + /// Returns null if no [RenderObject] is configured for the given slot. @protected - RenderBox? childForSlot(S slot) => _slotToChild[slot]; + ChildType? childForSlot(SlotType 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, + /// over the children of this [RenderObject]. 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; + Iterable<ChildType> get children => _slotToChild.values; /// Returns the debug name for a given `slot`. /// @@ -119,7 +144,7 @@ /// The default implementation calls [EnumName.name] on `slot` if it is an /// [Enum] value and `toString` if it is not. @protected - String debugNameForSlot(S slot) { + String debugNameForSlot(SlotType slot) { if (slot is Enum) { return slot.name; } @@ -129,7 +154,7 @@ @override void attach(PipelineOwner owner) { super.attach(owner); - for (final RenderBox child in children) { + for (final ChildType child in children) { child.attach(owner); } } @@ -137,7 +162,7 @@ @override void detach() { super.detach(); - for (final RenderBox child in children) { + for (final ChildType child in children) { child.detach(); } } @@ -155,24 +180,24 @@ @override List<DiagnosticsNode> debugDescribeChildren() { final List<DiagnosticsNode> value = <DiagnosticsNode>[]; - final Map<RenderBox, S> childToSlot = Map<RenderBox, S>.fromIterables( + final Map<ChildType, SlotType> childToSlot = Map<ChildType, SlotType>.fromIterables( _slotToChild.values, _slotToChild.keys, ); - for (final RenderBox child in children) { - _addDiagnostics(child, value, debugNameForSlot(childToSlot[child] as S)); + for (final ChildType child in children) { + _addDiagnostics(child, value, debugNameForSlot(childToSlot[child] as SlotType)); } return value; } - void _addDiagnostics(RenderBox child, List<DiagnosticsNode> value, String name) { + void _addDiagnostics(ChildType child, List<DiagnosticsNode> value, String name) { value.add(child.toDiagnosticsNode(name: name)); } - final Map<S, RenderBox> _slotToChild = <S, RenderBox>{}; + final Map<SlotType, ChildType> _slotToChild = <SlotType, ChildType>{}; - void _setChild(RenderBox? child, S slot) { - final RenderBox? oldChild = _slotToChild[slot]; + void _setChild(ChildType? child, SlotType slot) { + final ChildType? oldChild = _slotToChild[slot]; if (oldChild != null) { dropChild(oldChild); _slotToChild.remove(slot); @@ -183,9 +208,9 @@ } } - void _moveChild(RenderBox child, S slot, S oldSlot) { + void _moveChild(ChildType child, SlotType slot, SlotType oldSlot) { assert(slot != oldSlot); - final RenderBox? oldChild = _slotToChild[oldSlot]; + final ChildType? oldChild = _slotToChild[oldSlot]; if (oldChild == child) { _setChild(null, oldSlot); } @@ -193,16 +218,16 @@ } } -/// Element used by the [SlottedMultiChildRenderObjectWidgetMixin]. -class SlottedRenderObjectElement<S> extends RenderObjectElement { +/// Element used by the [SlottedMultiChildRenderObjectWidget]. +class SlottedRenderObjectElement<SlotType, ChildType extends RenderObject> extends RenderObjectElement { /// Creates an element that uses the given widget as its configuration. - SlottedRenderObjectElement(SlottedMultiChildRenderObjectWidgetMixin<S> super.widget); + SlottedRenderObjectElement(SlottedMultiChildRenderObjectWidgetMixin<SlotType, ChildType> super.widget); - Map<S, Element> _slotToChild = <S, Element>{}; + Map<SlotType, Element> _slotToChild = <SlotType, Element>{}; Map<Key, Element> _keyedChildren = <Key, Element>{}; @override - SlottedContainerRenderObjectMixin<S> get renderObject => super.renderObject as SlottedContainerRenderObjectMixin<S>; + SlottedContainerRenderObjectMixin<SlotType, ChildType> get renderObject => super.renderObject as SlottedContainerRenderObjectMixin<SlotType, ChildType>; @override void visitChildren(ElementVisitor visitor) { @@ -212,7 +237,7 @@ @override void forgetChild(Element child) { assert(_slotToChild.containsValue(child)); - assert(child.slot is S); + assert(child.slot is SlotType); assert(_slotToChild.containsKey(child.slot)); _slotToChild.remove(child.slot); super.forgetChild(child); @@ -225,16 +250,16 @@ } @override - void update(SlottedMultiChildRenderObjectWidgetMixin<S> newWidget) { + void update(SlottedMultiChildRenderObjectWidgetMixin<SlotType, ChildType> newWidget) { super.update(newWidget); assert(widget == newWidget); _updateChildren(); } - List<S>? _debugPreviousSlots; + List<SlotType>? _debugPreviousSlots; void _updateChildren() { - final SlottedMultiChildRenderObjectWidgetMixin<S> slottedMultiChildRenderObjectWidgetMixin = widget as SlottedMultiChildRenderObjectWidgetMixin<S>; + final SlottedMultiChildRenderObjectWidgetMixin<SlotType, ChildType> slottedMultiChildRenderObjectWidgetMixin = widget as SlottedMultiChildRenderObjectWidgetMixin<SlotType, ChildType>; assert(() { _debugPreviousSlots ??= slottedMultiChildRenderObjectWidgetMixin.slots.toList(); return listEquals(_debugPreviousSlots, slottedMultiChildRenderObjectWidgetMixin.slots.toList()); @@ -243,12 +268,12 @@ final Map<Key, Element> oldKeyedElements = _keyedChildren; _keyedChildren = <Key, Element>{}; - final Map<S, Element> oldSlotToChild = _slotToChild; - _slotToChild = <S, Element>{}; + final Map<SlotType, Element> oldSlotToChild = _slotToChild; + _slotToChild = <SlotType, Element>{}; Map<Key, List<Element>>? debugDuplicateKeys; - for (final S slot in slottedMultiChildRenderObjectWidgetMixin.slots) { + for (final SlotType slot in slottedMultiChildRenderObjectWidgetMixin.slots) { final Widget? widget = slottedMultiChildRenderObjectWidgetMixin.childForSlot(slot); final Key? newWidgetKey = widget?.key; @@ -259,7 +284,7 @@ // If key matching fails, resort to `oldSlotChild` from the same slot. final Element? fromElement; if (oldKeyChild != null) { - fromElement = oldSlotToChild.remove(oldKeyChild.slot as S); + fromElement = oldSlotToChild.remove(oldKeyChild.slot as SlotType); } else if (oldSlotChild?.widget.key == null) { fromElement = oldSlotToChild.remove(slot); } else { @@ -311,13 +336,13 @@ } @override - void insertRenderObjectChild(RenderBox child, S slot) { + void insertRenderObjectChild(ChildType child, SlotType slot) { renderObject._setChild(child, slot); assert(renderObject._slotToChild[slot] == child); } @override - void removeRenderObjectChild(RenderBox child, S slot) { + void removeRenderObjectChild(ChildType child, SlotType slot) { if (renderObject._slotToChild[slot] == child) { renderObject._setChild(null, slot); assert(renderObject._slotToChild[slot] == null); @@ -325,7 +350,7 @@ } @override - void moveRenderObjectChild(RenderBox child, S oldSlot, S newSlot) { + void moveRenderObjectChild(ChildType child, SlotType oldSlot, SlotType newSlot) { renderObject._moveChild(child, newSlot, oldSlot); } }
diff --git a/packages/flutter/test/widgets/slotted_render_object_widget_test.dart b/packages/flutter/test/widgets/slotted_render_object_widget_test.dart index e5ba7a5..7d61f84 100644 --- a/packages/flutter/test/widgets/slotted_render_object_widget_test.dart +++ b/packages/flutter/test/widgets/slotted_render_object_widget_test.dart
@@ -269,7 +269,7 @@ bottomRight, } -class _Diagonal extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<_DiagonalSlot?> { +class _Diagonal extends RenderObjectWidget with SlottedMultiChildRenderObjectWidgetMixin<_DiagonalSlot?, RenderBox> { const _Diagonal({ this.topLeft, this.bottomRight, @@ -296,14 +296,14 @@ } @override - SlottedContainerRenderObjectMixin<_DiagonalSlot?> createRenderObject( + SlottedContainerRenderObjectMixin<_DiagonalSlot?, RenderBox> createRenderObject( BuildContext context, ) { return _RenderDiagonal(); } } -class _RenderDiagonal extends RenderBox with SlottedContainerRenderObjectMixin<_DiagonalSlot?> { +class _RenderDiagonal extends RenderBox with SlottedContainerRenderObjectMixin<_DiagonalSlot?, RenderBox> { RenderBox? get _topLeft => childForSlot(_DiagonalSlot.topLeft); RenderBox? get _bottomRight => childForSlot(_DiagonalSlot.bottomRight); RenderBox? get _nullSlot => childForSlot(null); @@ -370,6 +370,6 @@ String toString() => describeIdentity(this); } -class _RenderTest extends RenderBox with SlottedContainerRenderObjectMixin<_Slot> { +class _RenderTest extends RenderBox with SlottedContainerRenderObjectMixin<_Slot, RenderBox> { String publicNameForSlot(_Slot slot) => debugNameForSlot(slot); }