blob: 48b000f5eb477473937bcb7eeadedcc90376483e [file] [log] [blame] [edit]
// 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_test/flutter_test.dart';
class TestItem extends StatelessWidget {
const TestItem({ super.key, required this.item, this.width, this.height });
final int item;
final double? width;
final double? height;
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
alignment: Alignment.center,
child: Text('Item $item', textDirection: TextDirection.ltr),
);
}
}
Widget buildFrame({ int? count, double? width, double? height, Axis? scrollDirection, Key? prototypeKey }) {
return Directionality(
textDirection: TextDirection.ltr,
child: CustomScrollView(
scrollDirection: scrollDirection ?? Axis.vertical,
slivers: <Widget>[
SliverPrototypeExtentList(
prototypeItem: TestItem(item: -1, width: width, height: height, key: prototypeKey),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) => TestItem(item: index),
childCount: count,
),
),
],
),
);
}
void main() {
testWidgets('SliverPrototypeExtentList.builder test', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverPrototypeExtentList.builder(
itemBuilder: (BuildContext context, int index) => TestItem(item: index),
prototypeItem: const TestItem(item: -1, height: 100.0),
itemCount: 20,
),
],
),
),
),
);
// The viewport is 600 pixels high, lazily created items are 100 pixels high.
for (int i = 0; i < 6; i += 1) {
final Finder item = find.widgetWithText(Container, 'Item $i');
expect(item, findsOneWidget);
expect(tester.getTopLeft(item).dy, i * 100.0);
expect(tester.getSize(item).height, 100.0);
}
for (int i = 7; i < 20; i += 1) {
expect(find.text('Item $i'), findsNothing);
}
});
testWidgets('SliverPrototypeExtentList.builder test', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverPrototypeExtentList.list(
prototypeItem: const TestItem(item: -1, height: 100.0),
children: <int>[0, 1, 2, 3, 4, 5, 6, 7].map((int index) => TestItem(item: index)).toList(),
),
],
),
),
),
);
// The viewport is 600 pixels high, lazily created items are 100 pixels high.
for (int i = 0; i < 6; i += 1) {
final Finder item = find.widgetWithText(Container, 'Item $i');
expect(item, findsOneWidget);
expect(tester.getTopLeft(item).dy, i * 100.0);
expect(tester.getSize(item).height, 100.0);
}
expect(find.text('Item 7'), findsNothing);
});
testWidgets('SliverPrototypeExtentList vertical scrolling basics', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame(count: 20, height: 100.0));
// The viewport is 600 pixels high, lazily created items are 100 pixels high.
for (int i = 0; i < 6; i += 1) {
final Finder item = find.widgetWithText(Container, 'Item $i');
expect(item, findsOneWidget);
expect(tester.getTopLeft(item).dy, i * 100.0);
expect(tester.getSize(item).height, 100.0);
}
for (int i = 7; i < 20; i += 1) {
expect(find.text('Item $i'), findsNothing);
}
// Fling scroll to the end.
await tester.fling(find.text('Item 2'), const Offset(0.0, -200.0), 5000.0);
await tester.pumpAndSettle();
for (int i = 19; i >= 14; i -= 1) {
expect(find.text('Item $i'), findsOneWidget);
}
for (int i = 13; i >= 0; i -= 1) {
expect(find.text('Item $i'), findsNothing);
}
});
testWidgets('SliverPrototypeExtentList horizontal scrolling basics', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame(count: 20, width: 100.0, scrollDirection: Axis.horizontal));
// The viewport is 800 pixels wide, lazily created items are 100 pixels wide.
for (int i = 0; i < 8; i += 1) {
final Finder item = find.widgetWithText(Container, 'Item $i');
expect(item, findsOneWidget);
expect(tester.getTopLeft(item).dx, i * 100.0);
expect(tester.getSize(item).width, 100.0);
}
for (int i = 9; i < 20; i += 1) {
expect(find.text('Item $i'), findsNothing);
}
// Fling scroll to the end.
await tester.fling(find.text('Item 3'), const Offset(-200.0, 0.0), 5000.0);
await tester.pumpAndSettle();
for (int i = 19; i >= 12; i -= 1) {
expect(find.text('Item $i'), findsOneWidget);
}
for (int i = 11; i >= 0; i -= 1) {
expect(find.text('Item $i'), findsNothing);
}
});
testWidgets('SliverPrototypeExtentList change the prototype item', (WidgetTester tester) async {
await tester.pumpWidget(buildFrame(count: 10, height: 60.0));
// The viewport is 600 pixels high, each of the 10 items is 60 pixels high
for (int i = 0; i < 10; i += 1) {
expect(find.text('Item $i'), findsOneWidget);
}
await tester.pumpWidget(buildFrame(count: 10, height: 120.0));
// Now the items are 120 pixels high, so only 5 fit.
for (int i = 0; i < 5; i += 1) {
expect(find.text('Item $i'), findsOneWidget);
}
for (int i = 5; i < 10; i += 1) {
expect(find.text('Item $i'), findsNothing);
}
await tester.pumpWidget(buildFrame(count: 10, height: 60.0));
// Now they all fit again
for (int i = 0; i < 10; i += 1) {
expect(find.text('Item $i'), findsOneWidget);
}
});
testWidgets('SliverPrototypeExtentList first item is also the prototype', (WidgetTester tester) async {
final List<Widget> items = List<Widget>.generate(10, (int index) {
return TestItem(key: ValueKey<int>(index), item: index, height: index == 0 ? 60.0 : null);
}).toList();
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: CustomScrollView(
slivers: <Widget>[
SliverPrototypeExtentList(
prototypeItem: items[0],
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) => items[index],
childCount: 10,
),
),
],
),
),
);
// Item 0 exists in the list and as the prototype item.
expect(tester.widgetList(find.text('Item 0', skipOffstage: false)).length, 2);
for (int i = 1; i < 10; i += 1) {
expect(find.text('Item $i'), findsOneWidget);
}
});
testWidgets('SliverPrototypeExtentList prototypeItem paint transform is zero.', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/67117
// This test ensures that the SliverPrototypeExtentList does not cause an
// assertion error when calculating the paint transform of its prototypeItem.
// The paint transform of the prototypeItem should be zero, since it is not visible.
final GlobalKey prototypeKey = GlobalKey();
await tester.pumpWidget(buildFrame(count: 20, height: 100.0, prototypeKey: prototypeKey));
final RenderObject scrollView = tester.renderObject(find.byType(CustomScrollView));
final RenderObject prototype = prototypeKey.currentContext!.findRenderObject()!;
expect(prototype.getTransformTo(scrollView), Matrix4.zero());
});
}