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
6a5964d8
Commit
6a5964d8
authored
Jan 13, 2020
by
Michael Goderbauer
Committed by
Flutter GitHub Bot
Jan 13, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make ParentDataWidget usable with different ancestor RenderObjectWidget types (#48541)
parent
8d438b03
Changes
9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
319 additions
and
140 deletions
+319
-140
action_sheet.dart
packages/flutter/lib/src/cupertino/action_sheet.dart
+4
-1
dialog.dart
packages/flutter/lib/src/cupertino/dialog.dart
+4
-1
automatic_keep_alive.dart
packages/flutter/lib/src/widgets/automatic_keep_alive.dart
+8
-7
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+12
-3
framework.dart
packages/flutter/lib/src/widgets/framework.dart
+122
-77
sliver.dart
packages/flutter/lib/src/widgets/sliver.dart
+4
-1
table.dart
packages/flutter/lib/src/widgets/table.dart
+4
-1
custom_multi_child_layout_test.dart
.../flutter/test/widgets/custom_multi_child_layout_test.dart
+4
-1
parent_data_test.dart
packages/flutter/test/widgets/parent_data_test.dart
+157
-48
No files found.
packages/flutter/lib/src/cupertino/action_sheet.dart
View file @
6a5964d8
...
...
@@ -955,7 +955,7 @@ class _PressableActionButtonState extends State<_PressableActionButton> {
// _ActionButtonParentData. _ActionButtonParentDataWidget is responsible for
// updating the pressed state of an _ActionButtonParentData based on the
// incoming isPressed property.
class
_ActionButtonParentDataWidget
extends
ParentDataWidget
<
_
CupertinoAlertActionsRenderWidget
>
{
class
_ActionButtonParentDataWidget
extends
ParentDataWidget
<
_
ActionButtonParentData
>
{
const
_ActionButtonParentDataWidget
({
Key
key
,
this
.
isPressed
,
...
...
@@ -977,6 +977,9 @@ class _ActionButtonParentDataWidget extends ParentDataWidget<_CupertinoAlertActi
targetParent
.
markNeedsPaint
();
}
}
@override
Type
get
debugTypicalAncestorWidgetClass
=>
_CupertinoAlertActionsRenderWidget
;
}
// ParentData applied to individual action buttons that report whether or not
...
...
packages/flutter/lib/src/cupertino/dialog.dart
View file @
6a5964d8
...
...
@@ -1020,7 +1020,7 @@ class _PressableActionButtonState extends State<_PressableActionButton> {
// _ActionButtonParentData. _ActionButtonParentDataWidget is responsible for
// updating the pressed state of an _ActionButtonParentData based on the
// incoming [isPressed] property.
class
_ActionButtonParentDataWidget
extends
ParentDataWidget
<
_
CupertinoDialogActionsRenderWidget
>
{
class
_ActionButtonParentDataWidget
extends
ParentDataWidget
<
_
ActionButtonParentData
>
{
const
_ActionButtonParentDataWidget
({
Key
key
,
this
.
isPressed
,
...
...
@@ -1042,6 +1042,9 @@ class _ActionButtonParentDataWidget extends ParentDataWidget<_CupertinoDialogAct
targetParent
.
markNeedsPaint
();
}
}
@override
Type
get
debugTypicalAncestorWidgetClass
=>
_CupertinoDialogActionsRenderWidget
;
}
// ParentData applied to individual action buttons that report whether or not
...
...
packages/flutter/lib/src/widgets/automatic_keep_alive.dart
View file @
6a5964d8
...
...
@@ -5,6 +5,7 @@
import
'dart:async'
;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/rendering.dart'
;
import
'package:flutter/scheduler.dart'
;
import
'framework.dart'
;
...
...
@@ -80,7 +81,7 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
handle
.
addListener
(
_handles
[
handle
]);
if
(!
_keepingAlive
)
{
_keepingAlive
=
true
;
final
ParentDataElement
<
SliverWithKeepAliveWidget
>
childElement
=
_getChildElement
();
final
ParentDataElement
<
KeepAliveParentDataMixin
>
childElement
=
_getChildElement
();
if
(
childElement
!=
null
)
{
// If the child already exists, update it synchronously.
_updateParentDataOfChild
(
childElement
);
...
...
@@ -92,7 +93,7 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
if
(!
mounted
)
{
return
;
}
final
ParentDataElement
<
SliverWithKeepAliveWidget
>
childElement
=
_getChildElement
();
final
ParentDataElement
<
KeepAliveParentDataMixin
>
childElement
=
_getChildElement
();
assert
(
childElement
!=
null
);
_updateParentDataOfChild
(
childElement
);
});
...
...
@@ -105,7 +106,7 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
///
/// While this widget is guaranteed to have a child, this may return null if
/// the first build of that child has not completed yet.
ParentDataElement
<
SliverWithKeepAliveWidget
>
_getChildElement
()
{
ParentDataElement
<
KeepAliveParentDataMixin
>
_getChildElement
()
{
assert
(
mounted
);
final
Element
element
=
context
as
Element
;
Element
childElement
;
...
...
@@ -131,12 +132,12 @@ class _AutomaticKeepAliveState extends State<AutomaticKeepAlive> {
element
.
visitChildren
((
Element
child
)
{
childElement
=
child
;
});
assert
(
childElement
==
null
||
childElement
is
ParentDataElement
<
SliverWithKeepAliveWidget
>);
return
childElement
as
ParentDataElement
<
SliverWithKeepAliveWidget
>;
assert
(
childElement
==
null
||
childElement
is
ParentDataElement
<
KeepAliveParentDataMixin
>);
return
childElement
as
ParentDataElement
<
KeepAliveParentDataMixin
>;
}
void
_updateParentDataOfChild
(
ParentDataElement
<
SliverWithKeepAliveWidget
>
childElement
)
{
childElement
.
applyWidgetOutOfTurn
(
build
(
context
)
as
ParentDataWidget
<
SliverWithKeepAliveWidget
>);
void
_updateParentDataOfChild
(
ParentDataElement
<
KeepAliveParentDataMixin
>
childElement
)
{
childElement
.
applyWidgetOutOfTurn
(
build
(
context
)
as
ParentDataWidget
<
KeepAliveParentDataMixin
>);
}
VoidCallback
_createCallback
(
Listenable
handle
)
{
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
6a5964d8
...
...
@@ -1910,7 +1910,7 @@ class CustomSingleChildLayout extends SingleChildRenderObjectWidget {
/// The [MultiChildLayoutDelegate.hasChild],
/// [MultiChildLayoutDelegate.layoutChild], and
/// [MultiChildLayoutDelegate.positionChild] methods use these identifiers.
class
LayoutId
extends
ParentDataWidget
<
CustomMultiChildLayout
>
{
class
LayoutId
extends
ParentDataWidget
<
MultiChildLayoutParentData
>
{
/// Marks a child with a layout identifier.
///
/// Both the child and the id arguments must not be null.
...
...
@@ -1940,6 +1940,9 @@ class LayoutId extends ParentDataWidget<CustomMultiChildLayout> {
}
}
@override
Type
get
debugTypicalAncestorWidgetClass
=>
CustomMultiChildLayout
;
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
...
...
@@ -3353,7 +3356,7 @@ class IndexedStack extends Stack {
/// * [PositionedTransition], which takes a provided [Animation] to transition
/// changes in the child's position over a given duration.
/// * [PositionedDirectional], which adapts to the ambient [Directionality].
class
Positioned
extends
ParentDataWidget
<
Stack
>
{
class
Positioned
extends
ParentDataWidget
<
Stack
ParentData
>
{
/// Creates a widget that controls where a child of a [Stack] is positioned.
///
/// Only two out of the three horizontal values ([left], [right],
...
...
@@ -3577,6 +3580,9 @@ class Positioned extends ParentDataWidget<Stack> {
}
}
@override
Type
get
debugTypicalAncestorWidgetClass
=>
Stack
;
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
...
...
@@ -4363,7 +4369,7 @@ class Column extends Flex {
/// * [Expanded], which forces the child to expand to fill the available space.
/// * [Spacer], a widget that takes up space proportional to it's flex value.
/// * The [catalog of layout widgets](https://flutter.dev/widgets/layout/).
class
Flexible
extends
ParentDataWidget
<
Flex
>
{
class
Flexible
extends
ParentDataWidget
<
Flex
ParentData
>
{
/// Creates a widget that controls how a child of a [Row], [Column], or [Flex]
/// flexes.
const
Flexible
({
...
...
@@ -4413,6 +4419,9 @@ class Flexible extends ParentDataWidget<Flex> {
}
}
@override
Type
get
debugTypicalAncestorWidgetClass
=>
Flex
;
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
...
...
packages/flutter/lib/src/widgets/framework.dart
View file @
6a5964d8
This diff is collapsed.
Click to expand it.
packages/flutter/lib/src/widgets/sliver.dart
View file @
6a5964d8
...
...
@@ -1554,7 +1554,7 @@ class _SliverOffstageElement extends SingleChildRenderObjectElement {
/// In practice, the simplest way to deal with these notifications is to mix
/// [AutomaticKeepAliveClientMixin] into one's [State]. See the documentation
/// for that mixin class for details.
class
KeepAlive
extends
ParentDataWidget
<
SliverWithKeepAliveWidget
>
{
class
KeepAlive
extends
ParentDataWidget
<
KeepAliveParentDataMixin
>
{
/// Marks a child as needing to remain alive.
///
/// The [child] and [keepAlive] arguments must not be null.
...
...
@@ -1590,6 +1590,9 @@ class KeepAlive extends ParentDataWidget<SliverWithKeepAliveWidget> {
@override
bool
debugCanApplyOutOfTurn
()
=>
keepAlive
;
@override
Type
get
debugTypicalAncestorWidgetClass
=>
SliverWithKeepAliveWidget
;
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
...
...
packages/flutter/lib/src/widgets/table.dart
View file @
6a5964d8
...
...
@@ -354,7 +354,7 @@ class _TableElement extends RenderObjectElement {
/// the [TableCell] widget to its enclosing [Table] must contain only
/// [TableRow]s, [StatelessWidget]s, or [StatefulWidget]s (not
/// other kinds of widgets, like [RenderObjectWidget]s).
class
TableCell
extends
ParentDataWidget
<
Table
>
{
class
TableCell
extends
ParentDataWidget
<
Table
CellParentData
>
{
/// Creates a widget that controls how a child of a [Table] is aligned.
const
TableCell
({
Key
key
,
...
...
@@ -376,6 +376,9 @@ class TableCell extends ParentDataWidget<Table> {
}
}
@override
Type
get
debugTypicalAncestorWidgetClass
=>
Table
;
@override
void
debugFillProperties
(
DiagnosticPropertiesBuilder
properties
)
{
super
.
debugFillProperties
(
properties
);
...
...
packages/flutter/test/widgets/custom_multi_child_layout_test.dart
View file @
6a5964d8
...
...
@@ -160,7 +160,7 @@ class InvalidConstraintsChildLayoutDelegate extends MultiChildLayoutDelegate {
bool
shouldRelayout
(
MultiChildLayoutDelegate
oldDelegate
)
=>
true
;
}
class
LayoutWithMissingId
extends
ParentDataWidget
<
CustomMultiChildLayout
>
{
class
LayoutWithMissingId
extends
ParentDataWidget
<
MultiChildLayoutParentData
>
{
const
LayoutWithMissingId
({
Key
key
,
@required
Widget
child
,
...
...
@@ -169,6 +169,9 @@ class LayoutWithMissingId extends ParentDataWidget<CustomMultiChildLayout> {
@override
void
applyParentData
(
RenderObject
renderObject
)
{}
@override
Type
get
debugTypicalAncestorWidgetClass
=>
CustomMultiChildLayout
;
}
void
main
(
)
{
...
...
packages/flutter/test/widgets/parent_data_test.dart
View file @
6a5964d8
...
...
@@ -49,7 +49,6 @@ final TestParentData kNonPositioned = TestParentData();
void
main
(
)
{
testWidgets
(
'ParentDataWidget control test'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Stack
(
textDirection:
TextDirection
.
ltr
,
...
...
@@ -251,33 +250,39 @@ void main() {
testWidgets
(
'ParentDataWidget conflicting data'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Stack
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
children:
const
<
Widget
>[
Positioned
(
top:
5.0
,
bottom:
8.0
,
child:
Positioned
(
top:
6.0
,
left:
7.0
,
child:
DecoratedBox
(
decoration:
kBoxDecorationB
),
child:
Stack
(
textDirection:
TextDirection
.
ltr
,
children:
const
<
Widget
>[
Positioned
(
top:
5.0
,
bottom:
8.0
,
child:
Positioned
(
top:
6.0
,
left:
7.0
,
child:
DecoratedBox
(
decoration:
kBoxDecorationB
),
),
),
)
,
]
,
]
,
)
,
),
);
dynamic
exception
=
tester
.
takeException
();
expect
(
exception
,
isFlutterError
);
expect
(
exception
.
toString
(),
equalsIgnoringHashCodes
(
'Incorrect use of ParentDataWidget.
\n
'
'Positioned widgets must be placed directly inside Stack widgets.
\n
'
'Positioned(no depth, left: 7.0, top: 6.0, dirty) has a Stack ancestor, but there are other widgets between them:
\n
'
'- Positioned(top: 5.0, bottom: 8.0) (this is a different Positioned than the one with the problem)
\n
'
'These widgets cannot come between a Positioned and its Stack.
\n
'
'The ownership chain for the parent of the offending Positioned was:
\n
'
' Positioned ← Stack ← [root]'
'The following ParentDataWidgets are providing parent data to the same RenderObject:
\n
'
'- Positioned(left: 7.0, top: 6.0) (typically placed directly inside a Stack widget)
\n
'
'- Positioned(top: 5.0, bottom: 8.0) (typically placed directly inside a Stack widget)
\n
'
'However, a RenderObject can only receive parent data from at most one ParentDataWidget.
\n
'
'Usually, this indicates that at least one of the offending ParentDataWidgets listed '
'above is not placed directly inside a compatible ancestor widget.
\n
'
'The ownership chain for the RenderObject that received the parent data was:
\n
'
' DecoratedBox ← Positioned ← Positioned ← Stack ← Directionality ← [root]'
),
);
...
...
@@ -286,15 +291,18 @@ void main() {
checkTree
(
tester
,
<
TestParentData
>[]);
await
tester
.
pumpWidget
(
Container
(
child:
Row
(
children:
const
<
Widget
>[
Positioned
(
top:
6.0
,
left:
7.0
,
child:
DecoratedBox
(
decoration:
kBoxDecorationB
),
),
],
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Container
(
child:
Row
(
children:
const
<
Widget
>[
Positioned
(
top:
6.0
,
left:
7.0
,
child:
DecoratedBox
(
decoration:
kBoxDecorationB
),
),
],
),
),
),
);
...
...
@@ -304,10 +312,14 @@ void main() {
exception
.
toString
(),
equalsIgnoringHashCodes
(
'Incorrect use of ParentDataWidget.
\n
'
'Positioned widgets must be placed inside Stack widgets.
\n
'
'Positioned(no depth, left: 7.0, top: 6.0, dirty) has no Stack ancestor at all.
\n
'
'The ownership chain for the parent of the offending Positioned was:
\n
'
' Row ← Container ← [root]'
'The ParentDataWidget Positioned(left: 7.0, top: 6.0) wants to apply ParentData of type '
'StackParentData to a RenderObject, which has been set up to accept ParentData of '
'incompatible type FlexParentData.
\n
'
'Usually, this means that the Positioned widget has the wrong ancestor RenderObjectWidget. '
'Typically, Positioned widgets are placed directly inside Stack widgets.
\n
'
'The offending Positioned is currently placed inside a Row widget.
\n
'
'The ownership chain for the RenderObject that received the incompatible parent data was:
\n
'
' DecoratedBox ← Positioned ← Row ← Container ← Directionality ← [root]'
),
);
...
...
@@ -377,17 +389,20 @@ void main() {
});
testWidgets
(
'Parent data invalid ancestor'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Row
(
children:
<
Widget
>[
Stack
(
textDirection:
TextDirection
.
ltr
,
children:
<
Widget
>[
Expanded
(
child:
Container
(),
),
],
),
],
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
Row
(
children:
<
Widget
>[
Stack
(
textDirection:
TextDirection
.
ltr
,
children:
<
Widget
>[
Expanded
(
child:
Container
(),
),
],
),
],
),
));
final
dynamic
exception
=
tester
.
takeException
();
...
...
@@ -396,13 +411,107 @@ void main() {
exception
.
toString
(),
equalsIgnoringHashCodes
(
'Incorrect use of ParentDataWidget.
\n
'
'Expanded widgets must be placed directly inside Flex widgets.
\n
'
'Expanded(no depth, flex: 1, dirty) has a Flex ancestor, but there are other widgets between them:
\n
'
'- Stack(alignment: AlignmentDirectional.topStart, textDirection: ltr, fit: loose, overflow: clip)
\n
'
'These widgets cannot come between a Expanded and its Flex.
\n
'
'The ownership chain for the parent of the offending Expanded was:
\n
'
' Stack ← Row ← [root]'
'The ParentDataWidget Expanded(flex: 1) wants to apply ParentData of type '
'FlexParentData to a RenderObject, which has been set up to accept ParentData of '
'incompatible type StackParentData.
\n
'
'Usually, this means that the Expanded widget has the wrong ancestor RenderObjectWidget. '
'Typically, Expanded widgets are placed directly inside Flex widgets.
\n
'
'The offending Expanded is currently placed inside a Stack widget.
\n
'
'The ownership chain for the RenderObject that received the incompatible parent data was:
\n
'
' LimitedBox ← Container ← Expanded ← Stack ← Row ← Directionality ← [root]'
),
);
});
testWidgets
(
'ParentDataWidget can be used with different ancestor RenderObjectWidgets'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
OneAncestorWidget
(
child:
Container
(),
),
);
DummyParentData
parentData
=
tester
.
renderObject
(
find
.
byType
(
Container
)).
parentData
as
DummyParentData
;
expect
(
parentData
.
string
,
isNull
);
await
tester
.
pumpWidget
(
OneAncestorWidget
(
child:
TestParentDataWidget
(
string:
'Foo'
,
child:
Container
(),
),
),
);
parentData
=
tester
.
renderObject
(
find
.
byType
(
Container
)).
parentData
as
DummyParentData
;
expect
(
parentData
.
string
,
'Foo'
);
await
tester
.
pumpWidget
(
AnotherAncestorWidget
(
child:
TestParentDataWidget
(
string:
'Bar'
,
child:
Container
(),
),
),
);
parentData
=
tester
.
renderObject
(
find
.
byType
(
Container
)).
parentData
as
DummyParentData
;
expect
(
parentData
.
string
,
'Bar'
);
});
}
class
TestParentDataWidget
extends
ParentDataWidget
<
DummyParentData
>
{
const
TestParentDataWidget
({
Key
key
,
this
.
string
,
Widget
child
,
})
:
super
(
key:
key
,
child:
child
);
final
String
string
;
@override
void
applyParentData
(
RenderObject
renderObject
)
{
assert
(
renderObject
.
parentData
is
DummyParentData
);
final
DummyParentData
parentData
=
renderObject
.
parentData
as
DummyParentData
;
parentData
.
string
=
string
;
}
@override
Type
get
debugTypicalAncestorWidgetClass
=>
OneAncestorWidget
;
}
class
DummyParentData
extends
ParentData
{
String
string
;
}
class
OneAncestorWidget
extends
SingleChildRenderObjectWidget
{
const
OneAncestorWidget
({
Key
key
,
Widget
child
,
})
:
super
(
key:
key
,
child:
child
);
@override
RenderOne
createRenderObject
(
BuildContext
context
)
=>
RenderOne
();
}
class
AnotherAncestorWidget
extends
SingleChildRenderObjectWidget
{
const
AnotherAncestorWidget
({
Key
key
,
Widget
child
,
})
:
super
(
key:
key
,
child:
child
);
@override
RenderAnother
createRenderObject
(
BuildContext
context
)
=>
RenderAnother
();
}
class
RenderOne
extends
RenderProxyBox
{
@override
void
setupParentData
(
RenderBox
child
)
{
if
(
child
.
parentData
is
!
DummyParentData
)
child
.
parentData
=
DummyParentData
();
}
}
class
RenderAnother
extends
RenderProxyBox
{
@override
void
setupParentData
(
RenderBox
child
)
{
if
(
child
.
parentData
is
!
DummyParentData
)
child
.
parentData
=
DummyParentData
();
}
}
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