mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
SliverList works with changing children that are keyed (#16724)
Fixes #14590.
This commit is contained in:
parent
cf5778810b
commit
1ba99b94f2
@ -738,6 +738,20 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
|
||||
final SliverMultiBoxAdaptorParentData oldParentData = child?.renderObject?.parentData;
|
||||
final Element newChild = super.updateChild(child, newWidget, newSlot);
|
||||
final SliverMultiBoxAdaptorParentData newParentData = newChild?.renderObject?.parentData;
|
||||
|
||||
// Preserve the old layoutOffset if the renderObject was swapped out.
|
||||
if (oldParentData != newParentData && oldParentData != null && newParentData != null) {
|
||||
newParentData.layoutOffset = oldParentData.layoutOffset;
|
||||
}
|
||||
|
||||
return newChild;
|
||||
}
|
||||
|
||||
@override
|
||||
void forgetChild(Element child) {
|
||||
assert(child != null);
|
||||
|
181
packages/flutter/test/widgets/sliver_list_test.dart
Normal file
181
packages/flutter/test/widgets/sliver_list_test.dart
Normal file
@ -0,0 +1,181 @@
|
||||
// Copyright 2018 The Chromium 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_test/flutter_test.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('SliverList reverse children (with keys)', (WidgetTester tester) async {
|
||||
final List<int> items = new List<int>.generate(20, (int i) => i);
|
||||
const double itemHeight = 300.0;
|
||||
const double viewportHeight = 500.0;
|
||||
|
||||
const double scrollPosition = 18 * itemHeight;
|
||||
final ScrollController controller = new ScrollController(initialScrollOffset: scrollPosition);
|
||||
|
||||
await tester.pumpWidget(_buildSliverList(
|
||||
items: items,
|
||||
controller: controller,
|
||||
itemHeight: itemHeight,
|
||||
viewportHeight: viewportHeight,
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.offset, scrollPosition);
|
||||
expect(find.text('Tile 0'), findsNothing);
|
||||
expect(find.text('Tile 1'), findsNothing);
|
||||
expect(find.text('Tile 18'), findsOneWidget);
|
||||
expect(find.text('Tile 19'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(_buildSliverList(
|
||||
items: items.reversed.toList(),
|
||||
controller: controller,
|
||||
itemHeight: itemHeight,
|
||||
viewportHeight: viewportHeight,
|
||||
));
|
||||
final int frames = await tester.pumpAndSettle();
|
||||
expect(frames, 1); // ensures that there is no (animated) bouncing of the scrollable
|
||||
|
||||
expect(controller.offset, scrollPosition);
|
||||
expect(find.text('Tile 19'), findsNothing);
|
||||
expect(find.text('Tile 18'), findsNothing);
|
||||
expect(find.text('Tile 1'), findsOneWidget);
|
||||
expect(find.text('Tile 0'), findsOneWidget);
|
||||
|
||||
controller.jumpTo(0.0);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.offset, 0.0);
|
||||
expect(find.text('Tile 19'), findsOneWidget);
|
||||
expect(find.text('Tile 18'), findsOneWidget);
|
||||
expect(find.text('Tile 1'), findsNothing);
|
||||
expect(find.text('Tile 0'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('SliverList replace children (with keys)', (WidgetTester tester) async {
|
||||
final List<int> items = new List<int>.generate(20, (int i) => i);
|
||||
const double itemHeight = 300.0;
|
||||
const double viewportHeight = 500.0;
|
||||
|
||||
const double scrollPosition = 18 * itemHeight;
|
||||
final ScrollController controller = new ScrollController(initialScrollOffset: scrollPosition);
|
||||
|
||||
await tester.pumpWidget(_buildSliverList(
|
||||
items: items,
|
||||
controller: controller,
|
||||
itemHeight: itemHeight,
|
||||
viewportHeight: viewportHeight,
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.offset, scrollPosition);
|
||||
expect(find.text('Tile 0'), findsNothing);
|
||||
expect(find.text('Tile 1'), findsNothing);
|
||||
expect(find.text('Tile 18'), findsOneWidget);
|
||||
expect(find.text('Tile 19'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(_buildSliverList(
|
||||
items: items.map((int i) => i + 100).toList(),
|
||||
controller: controller,
|
||||
itemHeight: itemHeight,
|
||||
viewportHeight: viewportHeight,
|
||||
));
|
||||
final int frames = await tester.pumpAndSettle();
|
||||
expect(frames, 1); // ensures that there is no (animated) bouncing of the scrollable
|
||||
|
||||
expect(controller.offset, scrollPosition);
|
||||
expect(find.text('Tile 0'), findsNothing);
|
||||
expect(find.text('Tile 1'), findsNothing);
|
||||
expect(find.text('Tile 18'), findsNothing);
|
||||
expect(find.text('Tile 19'), findsNothing);
|
||||
|
||||
expect(find.text('Tile 100'), findsNothing);
|
||||
expect(find.text('Tile 101'), findsNothing);
|
||||
expect(find.text('Tile 118'), findsOneWidget);
|
||||
expect(find.text('Tile 119'), findsOneWidget);
|
||||
|
||||
controller.jumpTo(0.0);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.offset, 0.0);
|
||||
expect(find.text('Tile 100'), findsOneWidget);
|
||||
expect(find.text('Tile 101'), findsOneWidget);
|
||||
expect(find.text('Tile 118'), findsNothing);
|
||||
expect(find.text('Tile 119'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('SliverList replace with shorter children list (with keys)', (WidgetTester tester) async {
|
||||
final List<int> items = new List<int>.generate(20, (int i) => i);
|
||||
const double itemHeight = 300.0;
|
||||
const double viewportHeight = 500.0;
|
||||
|
||||
final double scrollPosition = items.length * itemHeight - viewportHeight;
|
||||
final ScrollController controller = new ScrollController(initialScrollOffset: scrollPosition);
|
||||
|
||||
await tester.pumpWidget(_buildSliverList(
|
||||
items: items,
|
||||
controller: controller,
|
||||
itemHeight: itemHeight,
|
||||
viewportHeight: viewportHeight,
|
||||
));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(controller.offset, scrollPosition);
|
||||
expect(find.text('Tile 0'), findsNothing);
|
||||
expect(find.text('Tile 1'), findsNothing);
|
||||
expect(find.text('Tile 17'), findsNothing);
|
||||
expect(find.text('Tile 18'), findsOneWidget);
|
||||
expect(find.text('Tile 19'), findsOneWidget);
|
||||
|
||||
await tester.pumpWidget(_buildSliverList(
|
||||
items: items.sublist(0, items.length - 1),
|
||||
controller: controller,
|
||||
itemHeight: itemHeight,
|
||||
viewportHeight: viewportHeight,
|
||||
));
|
||||
final int frames = await tester.pumpAndSettle();
|
||||
expect(frames, greaterThan(1)); // ensure animation to bring tile17 into view
|
||||
|
||||
expect(controller.offset, scrollPosition - itemHeight);
|
||||
expect(find.text('Tile 0'), findsNothing);
|
||||
expect(find.text('Tile 1'), findsNothing);
|
||||
expect(find.text('Tile 17'), findsOneWidget);
|
||||
expect(find.text('Tile 18'), findsOneWidget);
|
||||
expect(find.text('Tile 19'), findsNothing);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildSliverList({
|
||||
List<int> items: const <int>[],
|
||||
ScrollController controller,
|
||||
double itemHeight: 500.0,
|
||||
double viewportHeight: 300.0,
|
||||
}) {
|
||||
return new Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new Center(
|
||||
child: new Container(
|
||||
height: viewportHeight,
|
||||
child: new CustomScrollView(
|
||||
controller: controller,
|
||||
slivers: <Widget>[
|
||||
new SliverList(
|
||||
delegate: new SliverChildBuilderDelegate(
|
||||
(BuildContext context, int i) {
|
||||
return new Container(
|
||||
key: new ValueKey<int>(items[i]),
|
||||
height: itemHeight,
|
||||
child: new Text('Tile ${items[i]}'),
|
||||
);
|
||||
},
|
||||
childCount: items.length,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue
Block a user