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
9e9a7286
Unverified
Commit
9e9a7286
authored
Jan 14, 2022
by
xubaolin
Committed by
GitHub
Jan 14, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix a [_ViewportElement] RenderObjectChild update bug (#96377)
parent
72df4801
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
122 additions
and
3 deletions
+122
-3
viewport.dart
packages/flutter/lib/src/widgets/viewport.dart
+47
-3
custom_scroll_view_test.dart
packages/flutter/test/widgets/custom_scroll_view_test.dart
+75
-0
No files found.
packages/flutter/lib/src/widgets/viewport.dart
View file @
9e9a7286
...
@@ -211,6 +211,9 @@ class _ViewportElement extends MultiChildRenderObjectElement {
...
@@ -211,6 +211,9 @@ class _ViewportElement extends MultiChildRenderObjectElement {
/// Creates an element that uses the given widget as its configuration.
/// Creates an element that uses the given widget as its configuration.
_ViewportElement
(
Viewport
widget
)
:
super
(
widget
);
_ViewportElement
(
Viewport
widget
)
:
super
(
widget
);
bool
_doingMountOrUpdate
=
false
;
int
?
_centerSlotIndex
;
@override
@override
Viewport
get
widget
=>
super
.
widget
as
Viewport
;
Viewport
get
widget
=>
super
.
widget
as
Viewport
;
...
@@ -219,26 +222,67 @@ class _ViewportElement extends MultiChildRenderObjectElement {
...
@@ -219,26 +222,67 @@ class _ViewportElement extends MultiChildRenderObjectElement {
@override
@override
void
mount
(
Element
?
parent
,
Object
?
newSlot
)
{
void
mount
(
Element
?
parent
,
Object
?
newSlot
)
{
assert
(!
_doingMountOrUpdate
);
_doingMountOrUpdate
=
true
;
super
.
mount
(
parent
,
newSlot
);
super
.
mount
(
parent
,
newSlot
);
_updateCenter
();
_updateCenter
();
assert
(
_doingMountOrUpdate
);
_doingMountOrUpdate
=
false
;
}
}
@override
@override
void
update
(
MultiChildRenderObjectWidget
newWidget
)
{
void
update
(
MultiChildRenderObjectWidget
newWidget
)
{
assert
(!
_doingMountOrUpdate
);
_doingMountOrUpdate
=
true
;
super
.
update
(
newWidget
);
super
.
update
(
newWidget
);
_updateCenter
();
_updateCenter
();
assert
(
_doingMountOrUpdate
);
_doingMountOrUpdate
=
false
;
}
}
void
_updateCenter
()
{
void
_updateCenter
()
{
// TODO(ianh): cache the keys to make this faster
// TODO(ianh): cache the keys to make this faster
if
(
widget
.
center
!=
null
)
{
if
(
widget
.
center
!=
null
)
{
renderObject
.
center
=
children
.
singleWhere
(
int
elementIndex
=
0
;
(
Element
element
)
=>
element
.
widget
.
key
==
widget
.
center
,
for
(
final
Element
e
in
children
)
{
).
renderObject
as
RenderSliver
?;
if
(
e
.
widget
.
key
==
widget
.
center
)
{
renderObject
.
center
=
e
.
renderObject
as
RenderSliver
?;
break
;
}
elementIndex
++;
}
assert
(
elementIndex
<
children
.
length
);
_centerSlotIndex
=
elementIndex
;
}
else
if
(
children
.
isNotEmpty
)
{
}
else
if
(
children
.
isNotEmpty
)
{
renderObject
.
center
=
children
.
first
.
renderObject
as
RenderSliver
?;
renderObject
.
center
=
children
.
first
.
renderObject
as
RenderSliver
?;
_centerSlotIndex
=
0
;
}
else
{
}
else
{
renderObject
.
center
=
null
;
renderObject
.
center
=
null
;
_centerSlotIndex
=
null
;
}
}
@override
void
insertRenderObjectChild
(
RenderObject
child
,
IndexedSlot
<
Element
?>
slot
)
{
super
.
insertRenderObjectChild
(
child
,
slot
);
// Once [mount]/[update] are done, the `renderObject.center` will be updated
// in [_updateCenter].
if
(!
_doingMountOrUpdate
&&
slot
.
index
==
_centerSlotIndex
)
{
renderObject
.
center
=
child
as
RenderSliver
?;
}
}
@override
void
moveRenderObjectChild
(
RenderObject
child
,
IndexedSlot
<
Element
?>
oldSlot
,
IndexedSlot
<
Element
?>
newSlot
)
{
super
.
moveRenderObjectChild
(
child
,
oldSlot
,
newSlot
);
assert
(
_doingMountOrUpdate
);
}
@override
void
removeRenderObjectChild
(
RenderObject
child
,
Object
?
slot
)
{
super
.
removeRenderObjectChild
(
child
,
slot
);
if
(!
_doingMountOrUpdate
&&
renderObject
.
center
==
child
)
{
renderObject
.
center
=
null
;
}
}
}
}
...
...
packages/flutter/test/widgets/custom_scroll_view_test.dart
View file @
9e9a7286
...
@@ -6,6 +6,81 @@ import 'package:flutter/widgets.dart';
...
@@ -6,6 +6,81 @@ import 'package:flutter/widgets.dart';
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
void
main
(
)
{
void
main
(
)
{
// Regression test for https://github.com/flutter/flutter/issues/96024
testWidgets
(
'CustomScrollView.center update test 1'
,
(
WidgetTester
tester
)
async
{
final
Key
centerKey
=
UniqueKey
();
late
StateSetter
setState
;
bool
hasKey
=
false
;
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CustomScrollView
(
center:
centerKey
,
slivers:
<
Widget
>[
const
SliverToBoxAdapter
(
key:
Key
(
'a'
),
child:
SizedBox
(
height:
100.0
)),
StatefulBuilder
(
key:
centerKey
,
builder:
(
BuildContext
context
,
StateSetter
setter
)
{
setState
=
setter
;
if
(
hasKey
)
{
return
const
SliverToBoxAdapter
(
key:
Key
(
'b'
),
child:
SizedBox
(
height:
100.0
),
);
}
else
{
return
const
SliverToBoxAdapter
(
child:
SizedBox
(
height:
100.0
),
);
}
},
),
],
),
));
await
tester
.
pumpAndSettle
();
// Change the center key will trigger the old RenderObject remove and a new
// RenderObject insert.
setState
(()
{
hasKey
=
true
;
});
await
tester
.
pumpAndSettle
();
// Pass without throw.
});
testWidgets
(
'CustomScrollView.center update test 2'
,
(
WidgetTester
tester
)
async
{
const
List
<
Widget
>
slivers1
=
<
Widget
>[
SliverToBoxAdapter
(
key:
Key
(
'a'
),
child:
SizedBox
(
height:
100.0
)),
SliverToBoxAdapter
(
key:
Key
(
'b'
),
child:
SizedBox
(
height:
100.0
)),
SliverToBoxAdapter
(
key:
Key
(
'c'
),
child:
SizedBox
(
height:
100.0
)),
];
const
List
<
Widget
>
slivers2
=
<
Widget
>[
SliverToBoxAdapter
(
key:
Key
(
'c'
),
child:
SizedBox
(
height:
100.0
)),
SliverToBoxAdapter
(
key:
Key
(
'd'
),
child:
SizedBox
(
height:
100.0
)),
SliverToBoxAdapter
(
key:
Key
(
'a'
),
child:
SizedBox
(
height:
100.0
)),
];
Widget
buildFrame
(
List
<
Widget
>
slivers
,
Key
center
)
{
return
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
CustomScrollView
(
center:
center
,
slivers:
slivers
,
),
);
}
await
tester
.
pumpWidget
(
buildFrame
(
slivers1
,
const
Key
(
'b'
)));
await
tester
.
pumpAndSettle
();
await
tester
.
pumpWidget
(
buildFrame
(
slivers2
,
const
Key
(
'd'
)));
await
tester
.
pumpAndSettle
();
// Pass without throw.
});
testWidgets
(
'CustomScrollView.center'
,
(
WidgetTester
tester
)
async
{
testWidgets
(
'CustomScrollView.center'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
const
Directionality
(
await
tester
.
pumpWidget
(
const
Directionality
(
textDirection:
TextDirection
.
ltr
,
textDirection:
TextDirection
.
ltr
,
...
...
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