Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
885a1482
Unverified
Commit
885a1482
authored
Dec 03, 2021
by
Michael Goderbauer
Committed by
GitHub
Dec 03, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "Mixin for slotted RenderObjectWidgets and RenderBox (#94077)" (#94620)
This reverts commit
988959da
.
parent
13f25ddd
Changes
11
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
629 additions
and
1002 deletions
+629
-1002
slotted_multi_child_render_object_widget_mixin.0.dart
...get/slotted_multi_child_render_object_widget_mixin.0.dart
+0
-289
slotted_multi_child_render_object_widget_mixin.0_test.dart
...lotted_multi_child_render_object_widget_mixin.0_test.dart
+0
-41
chip.dart
packages/flutter/lib/src/material/chip.dart
+176
-21
input_decorator.dart
packages/flutter/lib/src/material/input_decorator.dart
+267
-45
list_tile.dart
packages/flutter/lib/src/material/list_tile.dart
+184
-24
object.dart
packages/flutter/lib/src/rendering/object.dart
+0
-5
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+1
-12
slotted_render_object_widget.dart
...flutter/lib/src/widgets/slotted_render_object_widget.dart
+0
-271
widgets.dart
packages/flutter/lib/widgets.dart
+0
-1
chip_test.dart
packages/flutter/test/material/chip_test.dart
+1
-1
slotted_render_object_widget_test.dart
...utter/test/widgets/slotted_render_object_widget_test.dart
+0
-292
No files found.
examples/api/lib/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0.dart
deleted
100644 → 0
View file @
13f25ddd
// 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
());
}
examples/api/test/widgets/slotted_render_object_widget/slotted_multi_child_render_object_widget_mixin.0_test.dart
deleted
100644 → 0
View file @
13f25ddd
// 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
));
}
packages/flutter/lib/src/material/chip.dart
View file @
885a1482
...
...
@@ -2059,7 +2059,7 @@ class _RenderChipRedirectingHitDetection extends RenderConstrainedBox {
}
}
class
_ChipRenderWidget
extends
RenderObjectWidget
with
SlottedMultiChildRenderObjectWidgetMixin
<
_ChipSlot
>
{
class
_ChipRenderWidget
extends
RenderObjectWidget
{
const
_ChipRenderWidget
({
Key
?
key
,
required
this
.
theme
,
...
...
@@ -2083,19 +2083,7 @@ class _ChipRenderWidget extends RenderObjectWidget with SlottedMultiChildRenderO
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 @@ class _ChipRenderWidget extends RenderObjectWidget with SlottedMultiChildRenderO
}
@override
SlottedContainerRenderObjectMixin
<
_ChipSlot
>
createRenderObject
(
BuildContext
context
)
{
RenderObject
createRenderObject
(
BuildContext
context
)
{
return
_RenderChip
(
theme:
theme
,
textDirection:
Directionality
.
of
(
context
),
...
...
@@ -2133,6 +2121,105 @@ enum _ChipSlot {
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 _ChipRenderTheme {
}
}
class
_RenderChip
extends
RenderBox
with
SlottedContainerRenderObjectMixin
<
_ChipSlot
>
{
class
_RenderChip
extends
RenderBox
{
_RenderChip
({
required
_ChipRenderTheme
theme
,
required
TextDirection
textDirection
,
...
...
@@ -2220,6 +2307,8 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip
enableAnimation
.
addListener
(
markNeedsPaint
);
}
final
Map
<
_ChipSlot
,
RenderBox
>
children
=
<
_ChipSlot
,
RenderBox
>{};
bool
?
value
;
bool
?
isEnabled
;
late
Rect
_deleteButtonRect
;
...
...
@@ -2230,9 +2319,35 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip
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 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip
}
// The returned list is ordered for hit testing.
@override
Iterable
<
RenderBox
>
get
children
sync
*
{
Iterable
<
RenderBox
>
get
_children
sync
*
{
if
(
avatar
!=
null
)
{
yield
avatar
!;
}
...
...
@@ -2271,6 +2385,47 @@ class _RenderChip extends RenderBox with SlottedContainerRenderObjectMixin<_Chip
bool
get
isDrawingCheckmark
=>
theme
.
showCheckmark
&&
!
checkmarkAnimation
.
isDismissed
;
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
;
...
...
packages/flutter/lib/src/material/input_decorator.dart
View file @
885a1482
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/material/list_tile.dart
View file @
885a1482
...
...
@@ -1266,7 +1266,7 @@ enum _ListTileSlot {
trailing
,
}
class
_ListTile
extends
RenderObjectWidget
with
SlottedMultiChildRenderObjectWidgetMixin
<
_ListTileSlot
>
{
class
_ListTile
extends
RenderObjectWidget
{
const
_ListTile
({
Key
?
key
,
this
.
leading
,
...
...
@@ -1307,21 +1307,7 @@ class _ListTile extends RenderObjectWidget with SlottedMultiChildRenderObjectWid
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 _ListTile extends RenderObjectWidget with SlottedMultiChildRenderObjectWid
}
}
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 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_
_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
)
...
...
@@ -1493,6 +1615,44 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_
markNeedsLayout
();
}
@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
;
...
...
@@ -1745,7 +1905,7 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_
@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
,
...
...
packages/flutter/lib/src/rendering/object.dart
View file @
885a1482
...
...
@@ -3167,11 +3167,6 @@ mixin ContainerParentDataMixin<ChildType extends RenderObject> on ParentData {
/// 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
;
...
...
packages/flutter/lib/src/widgets/framework.dart
View file @
885a1482
...
...
@@ -1677,13 +1677,6 @@ abstract class InheritedWidget extends ProxyWidget {
/// 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 @@ abstract class SingleChildRenderObjectWidget extends RenderObjectWidget {
/// 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.
///
...
...
packages/flutter/lib/src/widgets/slotted_render_object_widget.dart
deleted
100644 → 0
View file @
13f25ddd
// 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'
);
}
}
packages/flutter/lib/widgets.dart
View file @
885a1482
...
...
@@ -116,7 +116,6 @@ export 'src/widgets/sliver_fill.dart';
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'
;
...
...
packages/flutter/test/material/chip_test.dart
View file @
885a1482
...
...
@@ -15,7 +15,7 @@ import '../widgets/semantics_tester.dart';
import
'feedback_tester.dart'
;
Finder
findRenderChipElement
(
)
{
return
find
.
byElementPredicate
((
Element
e
)
=>
'
${e.r
enderObject.runtimeType}
'
==
'_RenderChip
'
);
return
find
.
byElementPredicate
((
Element
e
)
=>
'
${e.r
untimeType}
'
==
'_RenderChipElement
'
);
}
RenderBox
getMaterialBox
(
WidgetTester
tester
)
{
...
...
packages/flutter/test/widgets/slotted_render_object_widget_test.dart
deleted
100644 → 0
View file @
13f25ddd
// 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
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment