Unverified Commit 5c5ddd1f authored by chunhtai's avatar chunhtai Committed by GitHub

Revert "Fix 25807: implement move for sliver multibox widget (#29188)" (#31497)

This reverts commit 77ab0b83.
parent 981e922a
......@@ -1116,7 +1116,6 @@ class _TabBarViewState extends State<TabBarView> {
TabController _controller;
PageController _pageController;
List<Widget> _children;
List<Widget> _childrenWithKey;
int _currentIndex;
int _warpUnderwayCount = 0;
......@@ -1158,7 +1157,7 @@ class _TabBarViewState extends State<TabBarView> {
@override
void initState() {
super.initState();
_updateChildren();
_children = widget.children;
}
@override
......@@ -1175,7 +1174,7 @@ class _TabBarViewState extends State<TabBarView> {
if (widget.controller != oldWidget.controller)
_updateTabController();
if (widget.children != oldWidget.children && _warpUnderwayCount == 0)
_updateChildren();
_children = widget.children;
}
@override
......@@ -1186,11 +1185,6 @@ class _TabBarViewState extends State<TabBarView> {
super.dispose();
}
void _updateChildren() {
_children = widget.children;
_childrenWithKey = KeyedSubtree.ensureUniqueKeysForList(widget.children);
}
void _handleTabControllerAnimationTick() {
if (_warpUnderwayCount > 0 || !_controller.indexIsChanging)
return; // This widget is driving the controller's animation.
......@@ -1213,30 +1207,28 @@ class _TabBarViewState extends State<TabBarView> {
return _pageController.animateToPage(_currentIndex, duration: kTabScrollDuration, curve: Curves.ease);
assert((_currentIndex - previousIndex).abs() > 1);
final int initialPage = _currentIndex > previousIndex
? _currentIndex - 1
: _currentIndex + 1;
final List<Widget> originalChildren = _childrenWithKey;
int initialPage;
setState(() {
_warpUnderwayCount += 1;
_childrenWithKey = List<Widget>.from(_childrenWithKey, growable: false);
final Widget temp = _childrenWithKey[initialPage];
_childrenWithKey[initialPage] = _childrenWithKey[previousIndex];
_childrenWithKey[previousIndex] = temp;
_children = List<Widget>.from(widget.children, growable: false);
if (_currentIndex > previousIndex) {
_children[_currentIndex - 1] = _children[previousIndex];
initialPage = _currentIndex - 1;
} else {
_children[_currentIndex + 1] = _children[previousIndex];
initialPage = _currentIndex + 1;
}
});
_pageController.jumpToPage(initialPage);
await _pageController.animateToPage(_currentIndex, duration: kTabScrollDuration, curve: Curves.ease);
if (!mounted)
return Future<void>.value();
setState(() {
_warpUnderwayCount -= 1;
if (widget.children != _children) {
_updateChildren();
} else {
_childrenWithKey = originalChildren;
}
_children = widget.children;
});
}
......@@ -1272,7 +1264,7 @@ class _TabBarViewState extends State<TabBarView> {
dragStartBehavior: widget.dragStartBehavior,
controller: _pageController,
physics: widget.physics == null ? _kTabBarViewPhysics : _kTabBarViewPhysics.applyTo(widget.physics),
children: _childrenWithKey,
children: _children,
),
);
}
......
......@@ -85,8 +85,7 @@ abstract class RenderSliverBoxChildManager {
/// list).
int get childCount;
/// Called during [RenderSliverMultiBoxAdaptor.adoptChild] or
/// [RenderSliverMultiBoxAdaptor.move].
/// Called during [RenderSliverMultiBoxAdaptor.adoptChild].
///
/// Subclasses must ensure that the [SliverMultiBoxAdaptorParentData.index]
/// field of the child's [RenderObject.parentData] accurately reflects the
......@@ -194,12 +193,7 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
RenderSliverMultiBoxAdaptor({
@required RenderSliverBoxChildManager childManager,
}) : assert(childManager != null),
_childManager = childManager {
assert(() {
_debugDanglingKeepAlives = <RenderBox>[];
return true;
}());
}
_childManager = childManager;
@override
void setupParentData(RenderObject child) {
......@@ -220,27 +214,6 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
/// The nodes being kept alive despite not being visible.
final Map<int, RenderBox> _keepAliveBucket = <int, RenderBox>{};
List<RenderBox> _debugDanglingKeepAlives;
/// Indicates whether integrity check is enabled.
///
/// Setting this property to true will immediately perform an integrity check.
///
/// The integrity check consists of:
///
/// 1. Verify that the children index in childList is in ascending order.
/// 2. Verify that there is no dangling keepalive child as the result of [move].
bool get debugChildIntegrityEnabled => _debugChildIntegrityEnabled;
bool _debugChildIntegrityEnabled = true;
set debugChildIntegrityEnabled(bool enabled) {
assert(enabled != null);
assert(() {
_debugChildIntegrityEnabled = enabled;
return _debugVerifyChildOrder() &&
(!_debugChildIntegrityEnabled || _debugDanglingKeepAlives.isEmpty);
}());
}
@override
void adoptChild(RenderObject child) {
super.adoptChild(child);
......@@ -251,70 +224,21 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
bool _debugAssertChildListLocked() => childManager.debugAssertChildListLocked();
/// Verify that the child list index is in strictly increasing order.
///
/// This has no effect in release builds.
bool _debugVerifyChildOrder(){
if (_debugChildIntegrityEnabled) {
RenderBox child = firstChild;
int index;
while (child != null) {
index = indexOf(child);
child = childAfter(child);
assert(child == null || indexOf(child) > index);
}
}
return true;
}
@override
void insert(RenderBox child, { RenderBox after }) {
assert(!_keepAliveBucket.containsValue(child));
super.insert(child, after: after);
assert(firstChild != null);
assert(_debugVerifyChildOrder());
}
@override
void move(RenderBox child, { RenderBox after }) {
// There are two scenarios:
//
// 1. The child is not keptAlive.
// The child is in the childList maintained by ContainerRenderObjectMixin.
// We can call super.move and update parentData with the new slot.
//
// 2. The child is keptAlive.
// In this case, the child is no longer in the childList but might be stored in
// [_keepAliveBucket]. We need to update the location of the child in the bucket.
final SliverMultiBoxAdaptorParentData childParentData = child.parentData;
if (!childParentData.keptAlive) {
super.move(child, after: after);
childManager.didAdoptChild(child); // updates the slot in the parentData
// Its slot may change even if super.move does not change the position.
// In this case, we still want to mark as needs layout.
markNeedsLayout();
} else {
// If the child in the bucket is not current child, that means someone has
// already moved and replaced current child, and we cannot remove this child.
if (_keepAliveBucket[childParentData.index] == child) {
_keepAliveBucket.remove(childParentData.index);
assert(() {
int index = indexOf(firstChild);
RenderBox child = childAfter(firstChild);
while (child != null) {
assert(indexOf(child) > index);
index = indexOf(child);
child = childAfter(child);
}
assert(() {
_debugDanglingKeepAlives.remove(child);
return true;
}());
// Update the slot and reinsert back to _keepAliveBucket in the new slot.
childManager.didAdoptChild(child);
// If there is an existing child in the new slot, that mean that child will
// be moved to other index. In other cases, the existing child should have been
// removed by updateChild. Thus, it is ok to overwrite it.
assert(() {
if (_keepAliveBucket.containsKey(childParentData.index))
_debugDanglingKeepAlives.add(_keepAliveBucket[childParentData.index]);
return true;
}());
_keepAliveBucket[childParentData.index] = child;
}
return true;
}());
}
@override
......@@ -325,10 +249,6 @@ abstract class RenderSliverMultiBoxAdaptor extends RenderSliver
return;
}
assert(_keepAliveBucket[childParentData.index] == child);
assert(() {
_debugDanglingKeepAlives.remove(child);
return true;
}());
_keepAliveBucket.remove(childParentData.index);
dropChild(child);
}
......
......@@ -149,13 +149,6 @@ abstract class GlobalKey<T extends State<StatefulWidget>> extends Key {
assert(() {
assert(parent != null);
if (_debugReservations.containsKey(this) && _debugReservations[this] != parent) {
// Reserving a new parent while the old parent is not attached is ok.
// This can happen when a renderObject detaches and re-attaches to rendering
// tree multiple times.
if (_debugReservations[this].renderObject?.attached == false) {
_debugReservations[this] = parent;
return true;
}
// It's possible for an element to get built multiple times in one
// frame, in which case it'll reserve the same child's key multiple
// times. We catch multiple children of one widget having the same key
......
......@@ -49,7 +49,6 @@ class StateMarkerState extends State<StateMarker> {
}
class AlwaysKeepAliveWidget extends StatefulWidget {
const AlwaysKeepAliveWidget({ Key key}) : super(key: key);
static String text = 'AlwaysKeepAlive';
@override
AlwaysKeepAliveState createState() => AlwaysKeepAliveState();
......@@ -1988,56 +1987,6 @@ void main() {
});
testWidgets('Skipping tabs with global key does not crash', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/24660
final List<String> tabs = <String>[
'Tab1',
'Tab2',
'Tab3',
'Tab4',
];
final TabController controller = TabController(
vsync: const TestVSync(),
length: tabs.length,
);
await tester.pumpWidget(
MaterialApp(
home: Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 300.0,
height: 200.0,
child: Scaffold(
appBar: AppBar(
title: const Text('tabs'),
bottom: TabBar(
controller: controller,
tabs: tabs.map<Widget>((String tab) => Tab(text: tab)).toList(),
),
),
body: TabBarView(
controller: controller,
children: <Widget>[
Text('1', key: GlobalKey()),
Text('2', key: GlobalKey()),
Text('3', key: GlobalKey()),
Text('4', key: GlobalKey()),
],
),
),
),
),
),
);
expect(find.text('1'), findsOneWidget);
expect(find.text('4'), findsNothing);
await tester.tap(find.text('Tab4'));
await tester.pumpAndSettle();
expect(controller.index, 3);
expect(find.text('4'), findsOneWidget);
expect(find.text('1'), findsNothing);
});
testWidgets('Skipping tabs with a KeepAlive child works', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/11895
final List<String> tabs = <String>[
......@@ -2069,7 +2018,7 @@ void main() {
body: TabBarView(
controller: controller,
children: <Widget>[
AlwaysKeepAliveWidget(key: UniqueKey()),
AlwaysKeepAliveWidget(),
const Text('2'),
const Text('3'),
const Text('4'),
......
......@@ -9,10 +9,10 @@ import 'package:flutter/widgets.dart';
void main() {
testWidgets('Sliver in a box', (WidgetTester tester) async {
await tester.pumpWidget(
DecoratedBox(
decoration: const BoxDecoration(),
const DecoratedBox(
decoration: BoxDecoration(),
child: SliverList(
delegate: SliverChildListDelegate(const <Widget>[]),
delegate: SliverChildListDelegate(<Widget>[]),
),
),
);
......@@ -21,9 +21,9 @@ void main() {
await tester.pumpWidget(
Row(
children: <Widget>[
children: const <Widget>[
SliverList(
delegate: SliverChildListDelegate(const <Widget>[]),
delegate: SliverChildListDelegate(<Widget>[]),
),
],
),
......
......@@ -451,41 +451,6 @@ void main() {
expect(count, 2);
});
testWidgets('GlobalKey - dettach and re-attach child to different parents', (WidgetTester tester) async {
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: Container(
height: 100,
child: CustomScrollView(
controller: ScrollController(),
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate(<Widget>[
Text('child', key: GlobalKey()),
]),
)
],
),
),
),
));
final SliverMultiBoxAdaptorElement element = tester.element(find.byType(SliverList));
Element childElement;
// Removing and recreating child with same Global Key should not trigger
// duplicate key error.
element.visitChildren((Element e) {
childElement = e;
});
element.removeChild(childElement.renderObject);
element.createChild(0, after: null);
element.visitChildren((Element e) {
childElement = e;
});
element.removeChild(childElement.renderObject);
element.createChild(0, after: null);
});
testWidgets('Defunct setState throws exception', (WidgetTester tester) async {
StateSetter setState;
......
......@@ -22,12 +22,12 @@ void main() {
data: const MediaQueryData(),
child: CustomScrollView(
controller: controller,
slivers: <Widget>[
const SliverAppBar(floating: true, pinned: true, expandedHeight: 200.0, title: Text('A')),
const SliverAppBar(primary: false, pinned: true, title: Text('B')),
slivers: const <Widget>[
SliverAppBar(floating: true, pinned: true, expandedHeight: 200.0, title: Text('A')),
SliverAppBar(primary: false, pinned: true, title: Text('B')),
SliverList(
delegate: SliverChildListDelegate(
const <Widget>[
<Widget>[
Text('C'),
Text('D'),
SizedBox(height: 500.0),
......
......@@ -203,9 +203,9 @@ void main() {
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverPersistentHeader(delegate: TestDelegate(), floating: true),
SliverList(
const SliverList(
delegate: SliverChildListDelegate(<Widget>[
const SizedBox(
SizedBox(
height: 300.0,
child: Text('X'),
),
......
......@@ -258,9 +258,9 @@ void main() {
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverPersistentHeader(delegate: TestDelegate(), pinned: true),
SliverList(
const SliverList(
delegate: SliverChildListDelegate(<Widget>[
const SizedBox(
SizedBox(
height: 300.0,
child: Text('X'),
),
......
......@@ -75,38 +75,6 @@ void main() {
});
testWidgets('Sliver appbars - scrolling - overscroll gap is below header', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverPersistentHeader(delegate: TestDelegate()),
SliverList(
delegate: SliverChildListDelegate(<Widget>[
const SizedBox(
height: 300.0,
child: Text('X'),
),
]),
),
],
),
),
);
expect(tester.getTopLeft(find.byType(Container)), Offset.zero);
expect(tester.getTopLeft(find.text('X')), const Offset(0.0, 200.0));
final ScrollPosition position = tester.state<ScrollableState>(find.byType(Scrollable)).position;
position.jumpTo(-50.0);
await tester.pump();
expect(tester.getTopLeft(find.byType(Container)), Offset.zero);
expect(tester.getTopLeft(find.text('X')), const Offset(0.0, 250.0));
});
testWidgets('Sliver appbars const child delegate - scrolling - overscroll gap is below header', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
......@@ -115,7 +83,7 @@ void main() {
slivers: <Widget>[
SliverPersistentHeader(delegate: TestDelegate()),
const SliverList(
delegate: SliverChildListDelegate.fixed(<Widget>[
delegate: SliverChildListDelegate(<Widget>[
SizedBox(
height: 300.0,
child: Text('X'),
......
......@@ -9,28 +9,6 @@ import 'package:flutter/widgets.dart';
import '../rendering/mock_canvas.dart';
Future<void> test(WidgetTester tester, double offset) {
return tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Viewport(
offset: ViewportOffset.fixed(offset),
slivers: <Widget>[
SliverList(
delegate: SliverChildListDelegate(const <Widget>[
SizedBox(height: 400.0, child: Text('a')),
SizedBox(height: 400.0, child: Text('b')),
SizedBox(height: 400.0, child: Text('c')),
SizedBox(height: 400.0, child: Text('d')),
SizedBox(height: 400.0, child: Text('e')),
]),
),
],
),
),
);
}
Future<void> testWithConstChildDelegate(WidgetTester tester, double offset) {
return tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
......@@ -38,7 +16,7 @@ Future<void> testWithConstChildDelegate(WidgetTester tester, double offset) {
offset: ViewportOffset.fixed(offset),
slivers: const <Widget>[
SliverList(
delegate: SliverChildListDelegate.fixed(<Widget>[
delegate: SliverChildListDelegate(<Widget>[
SizedBox(height: 400.0, child: Text('a')),
SizedBox(height: 400.0, child: Text('b')),
SizedBox(height: 400.0, child: Text('c')),
......@@ -98,39 +76,6 @@ void main() {
], 'ab');
});
testWidgets('Viewport+SliverBlock basic test with constant SliverChildListDelegate', (WidgetTester tester) async {
await testWithConstChildDelegate(tester, 0.0);
expect(tester.renderObject<RenderBox>(find.byType(Viewport)).size, equals(const Size(800.0, 600.0)));
verify(tester, <Offset>[
const Offset(0.0, 0.0),
const Offset(0.0, 400.0),
], 'ab');
await testWithConstChildDelegate(tester, 200.0);
verify(tester, <Offset>[
const Offset(0.0, -200.0),
const Offset(0.0, 200.0),
], 'ab');
await testWithConstChildDelegate(tester, 600.0);
verify(tester, <Offset>[
const Offset(0.0, -200.0),
const Offset(0.0, 200.0),
], 'bc');
await testWithConstChildDelegate(tester, 900.0);
verify(tester, <Offset>[
const Offset(0.0, -100.0),
const Offset(0.0, 300.0),
], 'cd');
await testWithConstChildDelegate(tester, 200.0);
verify(tester, <Offset>[
const Offset(0.0, -200.0),
const Offset(0.0, 200.0),
], 'ab');
});
testWidgets('Viewport with GlobalKey reparenting', (WidgetTester tester) async {
final Key key1 = GlobalKey();
final ViewportOffset offset = ViewportOffset.zero();
......@@ -205,9 +150,9 @@ void main() {
textDirection: TextDirection.ltr,
child: Viewport(
offset: offset,
slivers: <Widget>[
slivers: const <Widget>[
SliverList(
delegate: SliverChildListDelegate(const <Widget>[
delegate: SliverChildListDelegate(<Widget>[
SizedBox(height: 251.0, child: Text('a')),
SizedBox(height: 252.0, child: Text('b')),
]),
......@@ -316,9 +261,9 @@ void main() {
textDirection: TextDirection.ltr,
child: Viewport(
offset: ViewportOffset.zero(),
slivers: <Widget>[
slivers: const <Widget>[
SliverList(
delegate: SliverChildListDelegate(const <Widget>[
delegate: SliverChildListDelegate(<Widget>[
SizedBox(height: 400.0, child: Text('a')),
]),
),
......@@ -334,9 +279,9 @@ void main() {
textDirection: TextDirection.ltr,
child: Viewport(
offset: ViewportOffset.fixed(100.0),
slivers: <Widget>[
slivers: const <Widget>[
SliverList(
delegate: SliverChildListDelegate(const <Widget>[
delegate: SliverChildListDelegate(<Widget>[
SizedBox(height: 400.0, child: Text('a')),
]),
),
......@@ -352,9 +297,9 @@ void main() {
textDirection: TextDirection.ltr,
child: Viewport(
offset: ViewportOffset.fixed(100.0),
slivers: <Widget>[
slivers: const <Widget>[
SliverList(
delegate: SliverChildListDelegate(const <Widget>[
delegate: SliverChildListDelegate(<Widget>[
SizedBox(height: 4000.0, child: Text('a')),
]),
),
......@@ -370,9 +315,9 @@ void main() {
textDirection: TextDirection.ltr,
child: Viewport(
offset: ViewportOffset.zero(),
slivers: <Widget>[
slivers: const <Widget>[
SliverList(
delegate: SliverChildListDelegate(const <Widget>[
delegate: SliverChildListDelegate(<Widget>[
SizedBox(height: 4000.0, child: Text('a')),
]),
),
......
......@@ -206,8 +206,7 @@ void main() {
addRepaintBoundaries: false,
addSemanticIndexes: false,
);
final KeyedSubtree wrapped = builderThrowsDelegate.build(null, 0);
expect(wrapped.child, errorText);
expect(builderThrowsDelegate.build(null, 0), errorText);
expect(tester.takeException(), 'builder');
ErrorWidget.builder = oldBuilder;
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment