An example of parentData usage. (#131818)
This commit is contained in:
parent
e972d5a3f6
commit
f9a578dd82
394
examples/api/lib/rendering/box/parent_data.0.dart
Normal file
394
examples/api/lib/rendering/box/parent_data.0.dart
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
// 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';
|
||||||
|
|
||||||
|
void main() => runApp(const SampleApp());
|
||||||
|
|
||||||
|
class SampleApp extends StatefulWidget {
|
||||||
|
const SampleApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SampleApp> createState() => _SampleAppState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SampleAppState extends State<SampleApp> {
|
||||||
|
// This can be toggled using buttons in the UI to change which layout render object is used.
|
||||||
|
bool _compact = false;
|
||||||
|
|
||||||
|
// This is the content we show in the rendering.
|
||||||
|
//
|
||||||
|
// Headline and Paragraph are simple custom widgets defined below.
|
||||||
|
//
|
||||||
|
// Any widget _could_ be specified here, and would render fine.
|
||||||
|
// The Headline and Paragraph widgets are used so that the renderer
|
||||||
|
// can distinguish between the kinds of content and use different
|
||||||
|
// spacing between different children.
|
||||||
|
static const List<Widget> body = <Widget>[
|
||||||
|
Headline('Bugs that improve T for future bugs'),
|
||||||
|
Paragraph(
|
||||||
|
'The best bugs to fix are those that make us more productive '
|
||||||
|
'in the future. Reducing test flakiness, reducing technical '
|
||||||
|
'debt, increasing the number of team members who are able to '
|
||||||
|
'review code confidently and well: this all makes future bugs '
|
||||||
|
'easier to fix, which is a huge multiplier to our overall '
|
||||||
|
'effectiveness and thus to developer happiness.',
|
||||||
|
),
|
||||||
|
Headline('Bugs affecting more people are more valuable (maximize N)'),
|
||||||
|
Paragraph(
|
||||||
|
'We will make more people happier if we fix a bug experienced by more people.'
|
||||||
|
),
|
||||||
|
Paragraph(
|
||||||
|
'One thing to be careful about is to think about the number of '
|
||||||
|
'people we are ignoring in our metrics. For example, if we had '
|
||||||
|
'a bug that prevented our product from working on Windows, we '
|
||||||
|
'would have no Windows users, so the bug would affect nobody. '
|
||||||
|
'However, fixing the bug would enable millions of developers '
|
||||||
|
"to use our product, and that's the number that counts."
|
||||||
|
),
|
||||||
|
Headline('Bugs with greater impact on developers are more valuable (maximize ΔH)'),
|
||||||
|
Paragraph(
|
||||||
|
'A slight improvement to the user experience is less valuable '
|
||||||
|
'than a greater improvement. For example, if our application, '
|
||||||
|
'under certain conditions, shows a message with a typo, and '
|
||||||
|
'then crashes because of an off-by-one error in the code, '
|
||||||
|
'fixing the crash is a higher priority than fixing the typo.'
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
// This is the description of the demo's interface.
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Custom Render Boxes'),
|
||||||
|
// There are two buttons over to the top right of the demo that let you
|
||||||
|
// toggle between the two rendering modes.
|
||||||
|
actions: <Widget>[
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.density_small),
|
||||||
|
isSelected: _compact,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() { _compact = true; });
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(Icons.density_large),
|
||||||
|
isSelected: !_compact,
|
||||||
|
onPressed: () {
|
||||||
|
setState(() { _compact = false; });
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 20.0),
|
||||||
|
// CompactLayout and OpenLayout are the two rendering widgets defined below.
|
||||||
|
child: _compact ? const CompactLayout(children: body) : const OpenLayout(children: body),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Headline and Paragraph are just wrappers around the Text widget, but they
|
||||||
|
// also introduce a TextCategory widget that the CompactLayout and OpenLayout
|
||||||
|
// widgets can read to determine what kind of child is being rendered.
|
||||||
|
|
||||||
|
class Headline extends StatelessWidget {
|
||||||
|
const Headline(this.text, { super.key });
|
||||||
|
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextCategory(
|
||||||
|
category: 'headline',
|
||||||
|
child: Text(text, style: Theme.of(context).textTheme.titleLarge),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Paragraph extends StatelessWidget {
|
||||||
|
const Paragraph(this.text, { super.key });
|
||||||
|
|
||||||
|
final String text;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextCategory(
|
||||||
|
category: 'paragraph',
|
||||||
|
child: Text(text, style: Theme.of(context).textTheme.bodyLarge),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the ParentDataWidget that allows us to specify what kind of child
|
||||||
|
// is being rendered. It allows information to be shared with the render object
|
||||||
|
// without violating the principle of agnostic composition (wherein parents should
|
||||||
|
// work with any child, not only support a fixed set of children).
|
||||||
|
class TextCategory extends ParentDataWidget<TextFlowParentData> {
|
||||||
|
const TextCategory({ super.key, required this.category, required super.child });
|
||||||
|
|
||||||
|
final String category;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void applyParentData(RenderObject renderObject) {
|
||||||
|
final TextFlowParentData parentData = renderObject.parentData! as TextFlowParentData;
|
||||||
|
if (parentData.category != category) {
|
||||||
|
parentData.category = category;
|
||||||
|
renderObject.parent!.markNeedsLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Type get debugTypicalAncestorWidgetClass => OpenLayout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is one of the two layout variants. It is a widget that defers to
|
||||||
|
// a render object defined below (RenderCompactLayout).
|
||||||
|
class CompactLayout extends MultiChildRenderObjectWidget {
|
||||||
|
const CompactLayout({ super.key, super.children });
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderCompactLayout createRenderObject(BuildContext context) {
|
||||||
|
return RenderCompactLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateRenderObject(BuildContext context, RenderCompactLayout renderObject) {
|
||||||
|
// nothing to update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the other of the two layout variants. It is a widget that defers to a
|
||||||
|
// render object defined below (RenderOpenLayout).
|
||||||
|
class OpenLayout extends MultiChildRenderObjectWidget {
|
||||||
|
const OpenLayout({ super.key, super.children });
|
||||||
|
|
||||||
|
@override
|
||||||
|
RenderOpenLayout createRenderObject(BuildContext context) {
|
||||||
|
return RenderOpenLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateRenderObject(BuildContext context, RenderOpenLayout renderObject) {
|
||||||
|
// nothing to update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the data structure that contains the kind of data that can be
|
||||||
|
// passed to the parent to label the child. It is literally stored on
|
||||||
|
// the RenderObject child, in its "parentData" field.
|
||||||
|
class TextFlowParentData extends ContainerBoxParentData<RenderBox> {
|
||||||
|
String category = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the bulk of the layout logic. (It's similar to RenderListBody,
|
||||||
|
// but only supports vertical layout.) It has no properties.
|
||||||
|
//
|
||||||
|
// This is an abstract class that is then extended by RenderCompactLayout and
|
||||||
|
// RenderOpenLayout to get different layouts based on the children's categories,
|
||||||
|
// as stored in the ParentData structure defined above.
|
||||||
|
//
|
||||||
|
// The documentation for the RenderBox class and its members provides much
|
||||||
|
// more detail on how to implement each of the methods below.
|
||||||
|
abstract class RenderTextFlow extends RenderBox
|
||||||
|
with ContainerRenderObjectMixin<RenderBox, TextFlowParentData>,
|
||||||
|
RenderBoxContainerDefaultsMixin<RenderBox, TextFlowParentData> {
|
||||||
|
RenderTextFlow({ List<RenderBox>? children }) {
|
||||||
|
addAll(children);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void setupParentData(RenderBox child) {
|
||||||
|
if (child.parentData is! TextFlowParentData) {
|
||||||
|
child.parentData = TextFlowParentData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the function that is overridden by the subclasses to do the
|
||||||
|
// actual decision about the space to use between children.
|
||||||
|
double spacingBetween(String before, String after);
|
||||||
|
|
||||||
|
// The next few functions are the layout functions. In each case we walk the
|
||||||
|
// children, calling each one to determine the geometry of the child, and use
|
||||||
|
// that to determine the layout.
|
||||||
|
|
||||||
|
// The first two functions compute the intrinsic width of the render object,
|
||||||
|
// as seen when using the IntrinsicWidth widget.
|
||||||
|
//
|
||||||
|
// They essentially defer to the widest child.
|
||||||
|
|
||||||
|
@override
|
||||||
|
double computeMinIntrinsicWidth(double height) {
|
||||||
|
double width = 0.0;
|
||||||
|
RenderBox? child = firstChild;
|
||||||
|
while (child != null) {
|
||||||
|
final double childWidth = child.getMinIntrinsicWidth(height);
|
||||||
|
if (childWidth > width) {
|
||||||
|
width = childWidth;
|
||||||
|
}
|
||||||
|
child = childAfter(child);
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
double computeMaxIntrinsicWidth(double height) {
|
||||||
|
double width = 0.0;
|
||||||
|
RenderBox? child = firstChild;
|
||||||
|
while (child != null) {
|
||||||
|
final double childWidth = child.getMaxIntrinsicWidth(height);
|
||||||
|
if (childWidth > width) {
|
||||||
|
width = childWidth;
|
||||||
|
}
|
||||||
|
child = childAfter(child);
|
||||||
|
}
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The next two functions compute the intrinsic height of the render object,
|
||||||
|
// as seen when using the IntrinsicHeight widget.
|
||||||
|
//
|
||||||
|
// They add up the height contributed by each child.
|
||||||
|
//
|
||||||
|
// They have to take into account the categories of the children and the
|
||||||
|
// spacing that will be added, hence the slightly more elaborate logic.
|
||||||
|
|
||||||
|
@override
|
||||||
|
double computeMinIntrinsicHeight(double width) {
|
||||||
|
String? previousCategory;
|
||||||
|
double height = 0.0;
|
||||||
|
RenderBox? child = firstChild;
|
||||||
|
while (child != null) {
|
||||||
|
final String category = (child.parentData! as TextFlowParentData).category;
|
||||||
|
if (previousCategory != null) {
|
||||||
|
height += spacingBetween(previousCategory, category);
|
||||||
|
}
|
||||||
|
height += child.getMinIntrinsicHeight(width);
|
||||||
|
previousCategory = category;
|
||||||
|
child = childAfter(child);
|
||||||
|
}
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
double computeMaxIntrinsicHeight(double width) {
|
||||||
|
String? previousCategory;
|
||||||
|
double height = 0.0;
|
||||||
|
RenderBox? child = firstChild;
|
||||||
|
while (child != null) {
|
||||||
|
final String category = (child.parentData! as TextFlowParentData).category;
|
||||||
|
if (previousCategory != null) {
|
||||||
|
height += spacingBetween(previousCategory, category);
|
||||||
|
}
|
||||||
|
height += child.getMaxIntrinsicHeight(width);
|
||||||
|
previousCategory = category;
|
||||||
|
child = childAfter(child);
|
||||||
|
}
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function implements the baseline logic. Because this class does
|
||||||
|
// nothing special, we just defer to the default implementation in the
|
||||||
|
// RenderBoxContainerDefaultsMixin utility class.
|
||||||
|
|
||||||
|
@override
|
||||||
|
double? computeDistanceToActualBaseline(TextBaseline baseline) {
|
||||||
|
return defaultComputeDistanceToFirstActualBaseline(baseline);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next we have a function similar to the intrinsic methods, but for both axes
|
||||||
|
// at the same time.
|
||||||
|
|
||||||
|
@override
|
||||||
|
Size computeDryLayout(BoxConstraints constraints) {
|
||||||
|
final BoxConstraints innerConstraints = BoxConstraints.tightFor(width: constraints.maxWidth);
|
||||||
|
String? previousCategory;
|
||||||
|
double y = 0.0;
|
||||||
|
RenderBox? child = firstChild;
|
||||||
|
while (child != null) {
|
||||||
|
final String category = (child.parentData! as TextFlowParentData).category;
|
||||||
|
if (previousCategory != null) {
|
||||||
|
y += spacingBetween(previousCategory, category);
|
||||||
|
}
|
||||||
|
final Size childSize = child.getDryLayout(innerConstraints);
|
||||||
|
y += childSize.height;
|
||||||
|
previousCategory = category;
|
||||||
|
child = childAfter(child);
|
||||||
|
}
|
||||||
|
return constraints.constrain(Size(constraints.maxWidth, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the core of the layout logic. Most of the time, this is the only
|
||||||
|
// function that will be called. It computes the size and position of each
|
||||||
|
// child, and stores it (in the parent data, as it happens!) for use during
|
||||||
|
// the paint phase.
|
||||||
|
|
||||||
|
@override
|
||||||
|
void performLayout() {
|
||||||
|
final BoxConstraints innerConstraints = BoxConstraints.tightFor(width: constraints.maxWidth);
|
||||||
|
String? previousCategory;
|
||||||
|
double y = 0.0;
|
||||||
|
RenderBox? child = firstChild;
|
||||||
|
while (child != null) {
|
||||||
|
final String category = (child.parentData! as TextFlowParentData).category;
|
||||||
|
if (previousCategory != null) {
|
||||||
|
// This is where we call the function that computes the spacing between
|
||||||
|
// the different children. The arguments are the categories, obtained
|
||||||
|
// from the parentData property of each child.
|
||||||
|
y += spacingBetween(previousCategory, category);
|
||||||
|
}
|
||||||
|
child.layout(innerConstraints, parentUsesSize: true);
|
||||||
|
(child.parentData! as TextFlowParentData).offset = Offset(0.0, y);
|
||||||
|
y += child.size.height;
|
||||||
|
previousCategory = category;
|
||||||
|
child = childAfter(child);
|
||||||
|
}
|
||||||
|
size = constraints.constrain(Size(constraints.maxWidth, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hit testing is normal for this widget, so we defer to the default implementation.
|
||||||
|
@override
|
||||||
|
bool hitTestChildren(BoxHitTestResult result, { required Offset position }) {
|
||||||
|
return defaultHitTestChildren(result, position: position);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Painting is normal for this widget, so we defer to the default
|
||||||
|
// implementation. The default implementation expects to find the positions
|
||||||
|
// configured in the parentData property of each child, which is why we
|
||||||
|
// configure it that way in performLayout above.
|
||||||
|
@override
|
||||||
|
void paint(PaintingContext context, Offset offset) {
|
||||||
|
defaultPaint(context, offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally we have the two render objects that implement the two layouts in this demo.
|
||||||
|
|
||||||
|
class RenderOpenLayout extends RenderTextFlow {
|
||||||
|
@override
|
||||||
|
double spacingBetween(String before, String after) {
|
||||||
|
if (after == 'headline') {
|
||||||
|
return 20.0;
|
||||||
|
}
|
||||||
|
if (before == 'headline') {
|
||||||
|
return 5.0;
|
||||||
|
}
|
||||||
|
return 10.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RenderCompactLayout extends RenderTextFlow {
|
||||||
|
@override
|
||||||
|
double spacingBetween(String before, String after) {
|
||||||
|
if (after == 'headline') {
|
||||||
|
return 4.0;
|
||||||
|
}
|
||||||
|
return 2.0;
|
||||||
|
}
|
||||||
|
}
|
17
examples/api/test/rendering/box/parent_data.0_test.dart
Normal file
17
examples/api/test/rendering/box/parent_data.0_test.dart
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// 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_api_samples/rendering/box/parent_data.0.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('parent data example', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(const SampleApp());
|
||||||
|
expect(tester.getTopLeft(find.byType(Headline).at(2)), const Offset(30.0, 728.0));
|
||||||
|
await tester.tap(find.byIcon(Icons.density_small));
|
||||||
|
await tester.pump();
|
||||||
|
expect(tester.getTopLeft(find.byType(Headline).at(2)), const Offset(30.0, 682.0));
|
||||||
|
});
|
||||||
|
}
|
@ -911,6 +911,15 @@ class BoxHitTestEntry extends HitTestEntry<RenderBox> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parent data used by [RenderBox] and its subclasses.
|
/// Parent data used by [RenderBox] and its subclasses.
|
||||||
|
///
|
||||||
|
/// {@tool dartpad}
|
||||||
|
/// Parent data is used to communicate to a render object about its
|
||||||
|
/// children. In this example, there are two render objects that perform
|
||||||
|
/// text layout. They use parent data to identify the kind of child they
|
||||||
|
/// are laying out, and space the children accordingly.
|
||||||
|
///
|
||||||
|
/// ** See code in examples/api/lib/rendering/box/parent_data.0.dart **
|
||||||
|
/// {@end-tool}
|
||||||
class BoxParentData extends ParentData {
|
class BoxParentData extends ParentData {
|
||||||
/// The offset at which to paint the child in the parent's coordinate system.
|
/// The offset at which to paint the child in the parent's coordinate system.
|
||||||
Offset offset = Offset.zero;
|
Offset offset = Offset.zero;
|
||||||
|
@ -1778,16 +1778,20 @@ abstract class InheritedWidget extends ProxyWidget {
|
|||||||
bool updateShouldNotify(covariant InheritedWidget oldWidget);
|
bool updateShouldNotify(covariant InheritedWidget oldWidget);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RenderObjectWidgets provide the configuration for [RenderObjectElement]s,
|
/// [RenderObjectWidget]s provide the configuration for [RenderObjectElement]s,
|
||||||
/// which wrap [RenderObject]s, which provide the actual rendering of the
|
/// which wrap [RenderObject]s, which provide the actual rendering of the
|
||||||
/// application.
|
/// application.
|
||||||
///
|
///
|
||||||
/// See also:
|
/// Usually, rather than subclassing [RenderObjectWidget] directly, render
|
||||||
|
/// object widgets subclass one of:
|
||||||
///
|
///
|
||||||
/// * [MultiChildRenderObjectWidget], which configures a [RenderObject] with
|
/// * [LeafRenderObjectWidget], if the widget has no children.
|
||||||
/// a single list of children.
|
/// * [SingleChildRenderObjectElement], if the widget has exactly one child.
|
||||||
/// * [SlottedMultiChildRenderObjectWidget], which configures a
|
/// * [MultiChildRenderObjectWidget], if the widget takes a list of children.
|
||||||
/// [RenderObject] that organizes its children in different named slots.
|
/// * [SlottedMultiChildRenderObjectWidget], if the widget organizes its
|
||||||
|
/// children in different named slots.
|
||||||
|
///
|
||||||
|
/// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
||||||
abstract class RenderObjectWidget extends Widget {
|
abstract class RenderObjectWidget extends Widget {
|
||||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||||
/// const constructors so that they can be used in const expressions.
|
/// const constructors so that they can be used in const expressions.
|
||||||
@ -1830,8 +1834,10 @@ abstract class RenderObjectWidget extends Widget {
|
|||||||
void didUnmountRenderObject(covariant RenderObject renderObject) { }
|
void didUnmountRenderObject(covariant RenderObject renderObject) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A superclass for RenderObjectWidgets that configure RenderObject subclasses
|
/// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses
|
||||||
/// that have no children.
|
/// that have no children.
|
||||||
|
///
|
||||||
|
/// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
||||||
abstract class LeafRenderObjectWidget extends RenderObjectWidget {
|
abstract class LeafRenderObjectWidget extends RenderObjectWidget {
|
||||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||||
/// const constructors so that they can be used in const expressions.
|
/// const constructors so that they can be used in const expressions.
|
||||||
@ -1842,13 +1848,14 @@ abstract class LeafRenderObjectWidget extends RenderObjectWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses
|
/// A superclass for [RenderObjectWidget]s that configure [RenderObject] subclasses
|
||||||
/// that have a single child slot. (This superclass only provides the storage
|
/// that have a single child slot.
|
||||||
/// for that child, it doesn't actually provide the updating logic.)
|
|
||||||
///
|
///
|
||||||
/// Typically, the render object assigned to this widget will make use of
|
/// The render object assigned to this widget should make use of
|
||||||
/// [RenderObjectWithChildMixin] to implement a single-child model. The mixin
|
/// [RenderObjectWithChildMixin] to implement a single-child model. The mixin
|
||||||
/// exposes a [RenderObjectWithChildMixin.child] property that allows
|
/// exposes a [RenderObjectWithChildMixin.child] property that allows retrieving
|
||||||
/// retrieving the render object belonging to the [child] widget.
|
/// the render object belonging to the [child] widget.
|
||||||
|
///
|
||||||
|
/// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
||||||
abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
|
abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
|
||||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||||
/// const constructors so that they can be used in const expressions.
|
/// const constructors so that they can be used in const expressions.
|
||||||
@ -1868,13 +1875,15 @@ abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
|
|||||||
/// storage for that child list, it doesn't actually provide the updating
|
/// storage for that child list, it doesn't actually provide the updating
|
||||||
/// logic.)
|
/// logic.)
|
||||||
///
|
///
|
||||||
/// Subclasses must return a [RenderObject] that mixes in
|
/// Subclasses must use a [RenderObject] that mixes in
|
||||||
/// [ContainerRenderObjectMixin], which provides the necessary functionality to
|
/// [ContainerRenderObjectMixin], which provides the necessary functionality to
|
||||||
/// visit the children of the container render object (the render object
|
/// visit the children of the container render object (the render object
|
||||||
/// belonging to the [children] widgets). Typically, subclasses will return a
|
/// belonging to the [children] widgets). Typically, subclasses will use a
|
||||||
/// [RenderBox] that mixes in both [ContainerRenderObjectMixin] and
|
/// [RenderBox] that mixes in both [ContainerRenderObjectMixin] and
|
||||||
/// [RenderBoxContainerDefaultsMixin].
|
/// [RenderBoxContainerDefaultsMixin].
|
||||||
///
|
///
|
||||||
|
/// Subclasses must implement [createRenderObject] and [updateRenderObject].
|
||||||
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
/// * [Stack], which uses [MultiChildRenderObjectWidget].
|
/// * [Stack], which uses [MultiChildRenderObjectWidget].
|
||||||
@ -6538,8 +6547,8 @@ class LeafRenderObjectElement extends RenderObjectElement {
|
|||||||
///
|
///
|
||||||
/// The child is optional.
|
/// The child is optional.
|
||||||
///
|
///
|
||||||
/// This element subclass can be used for RenderObjectWidgets whose
|
/// This element subclass can be used for [RenderObjectWidget]s whose
|
||||||
/// RenderObjects use the [RenderObjectWithChildMixin] mixin. Such widgets are
|
/// [RenderObject]s use the [RenderObjectWithChildMixin] mixin. Such widgets are
|
||||||
/// expected to inherit from [SingleChildRenderObjectWidget].
|
/// expected to inherit from [SingleChildRenderObjectWidget].
|
||||||
class SingleChildRenderObjectElement extends RenderObjectElement {
|
class SingleChildRenderObjectElement extends RenderObjectElement {
|
||||||
/// Creates an element that uses the given widget as its configuration.
|
/// Creates an element that uses the given widget as its configuration.
|
||||||
@ -6600,8 +6609,8 @@ class SingleChildRenderObjectElement extends RenderObjectElement {
|
|||||||
|
|
||||||
/// An [Element] that uses a [MultiChildRenderObjectWidget] as its configuration.
|
/// An [Element] that uses a [MultiChildRenderObjectWidget] as its configuration.
|
||||||
///
|
///
|
||||||
/// This element subclass can be used for RenderObjectWidgets whose
|
/// This element subclass can be used for [RenderObjectWidget]s whose
|
||||||
/// RenderObjects use the [ContainerRenderObjectMixin] mixin with a parent data
|
/// [RenderObject]s use the [ContainerRenderObjectMixin] mixin with a parent data
|
||||||
/// type that implements [ContainerParentDataMixin<RenderObject>]. Such widgets
|
/// type that implements [ContainerParentDataMixin<RenderObject>]. Such widgets
|
||||||
/// are expected to inherit from [MultiChildRenderObjectWidget].
|
/// are expected to inherit from [MultiChildRenderObjectWidget].
|
||||||
///
|
///
|
||||||
|
Loading…
x
Reference in New Issue
Block a user