Unverified Commit 6f3168c4 authored by Remi Rousselet's avatar Remi Rousselet Committed by GitHub

Re-add the ability to return null in ListView.builder (#108706)

parent 6157c0e8
......@@ -675,6 +675,8 @@ class PageView extends StatefulWidget {
/// [itemBuilder] will be called only with indices greater than or equal to
/// zero and less than [itemCount].
///
/// {@macro flutter.widgets.ListView.builder.itemBuilder}
///
/// {@template flutter.widgets.PageView.findChildIndexCallback}
/// The [findChildIndexCallback] corresponds to the
/// [SliverChildBuilderDelegate.findChildIndexCallback] property. If null,
......@@ -693,7 +695,7 @@ class PageView extends StatefulWidget {
this.physics,
this.pageSnapping = true,
this.onPageChanged,
required IndexedWidgetBuilder itemBuilder,
required NullableIndexedWidgetBuilder itemBuilder,
ChildIndexGetter? findChildIndexCallback,
int? itemCount,
this.dragStartBehavior = DragStartBehavior.start,
......
......@@ -1114,13 +1114,22 @@ class ListView extends BoxScrollView {
/// The `itemBuilder` callback will be called only with indices greater than
/// or equal to zero and less than `itemCount`.
///
/// The `itemBuilder` should always return a non-null widget, and actually
/// create the widget instances when called. Avoid using a builder that
/// returns a previously-constructed widget; if the list view's children are
/// created in advance, or all at once when the [ListView] itself is created,
/// it is more efficient to use the [ListView] constructor. Even more
/// efficient, however, is to create the instances on demand using this
/// constructor's `itemBuilder` callback.
/// {@template flutter.widgets.ListView.builder.itemBuilder}
/// It is legal for `itemBuilder` to return `null`. If it does, the scroll view
/// will stop calling `itemBuilder`, even if it has yet to reach `itemCount`.
/// By returning `null`, the [ScrollPosition.maxScrollExtent] will not be accurate
/// unless the user has reached the end of the [ScrollView]. This can also cause the
/// [Scrollbar] to grow as the user scrolls.
///
/// For more accurate [ScrollMetrics], consider specifying `itemCount`.
/// {@endtemplate}
///
/// The `itemBuilder` should always create the widget instances when called.
/// Avoid using a builder that returns a previously-constructed widget; if the
/// list view's children are created in advance, or all at once when the
/// [ListView] itself is created, it is more efficient to use the [ListView]
/// constructor. Even more efficient, however, is to create the instances on
/// demand using this constructor's `itemBuilder` callback.
///
/// {@macro flutter.widgets.PageView.findChildIndexCallback}
///
......@@ -1142,7 +1151,7 @@ class ListView extends BoxScrollView {
super.padding,
this.itemExtent,
this.prototypeItem,
required IndexedWidgetBuilder itemBuilder,
required NullableIndexedWidgetBuilder itemBuilder,
ChildIndexGetter? findChildIndexCallback,
int? itemCount,
bool addAutomaticKeepAlives = true,
......@@ -1188,11 +1197,13 @@ class ListView extends BoxScrollView {
/// The `separatorBuilder` callback will be called with indices greater than
/// or equal to zero and less than `itemCount - 1`.
///
/// The `itemBuilder` and `separatorBuilder` callbacks should always return a
/// non-null widget, and actually create widget instances when called. Avoid
/// using a builder that returns a previously-constructed widget; if the list
/// view's children are created in advance, or all at once when the [ListView]
/// itself is created, it is more efficient to use the [ListView] constructor.
/// The `itemBuilder` and `separatorBuilder` callbacks should always
/// actually create widget instances when called. Avoid using a builder that
/// returns a previously-constructed widget; if the list view's children are
/// created in advance, or all at once when the [ListView] itself is created,
/// it is more efficient to use the [ListView] constructor.
///
/// {@macro flutter.widgets.ListView.builder.itemBuilder}
///
/// {@macro flutter.widgets.PageView.findChildIndexCallback}
///
......@@ -1230,7 +1241,7 @@ class ListView extends BoxScrollView {
super.physics,
super.shrinkWrap,
super.padding,
required IndexedWidgetBuilder itemBuilder,
required NullableIndexedWidgetBuilder itemBuilder,
ChildIndexGetter? findChildIndexCallback,
required IndexedWidgetBuilder separatorBuilder,
required int itemCount,
......@@ -1250,7 +1261,7 @@ class ListView extends BoxScrollView {
childrenDelegate = SliverChildBuilderDelegate(
(BuildContext context, int index) {
final int itemIndex = index ~/ 2;
final Widget widget;
final Widget? widget;
if (index.isEven) {
widget = itemBuilder(context, itemIndex);
} else {
......@@ -1742,6 +1753,8 @@ class GridView extends BoxScrollView {
/// `itemBuilder` will be called only with indices greater than or equal to
/// zero and less than `itemCount`.
///
/// {@macro flutter.widgets.ListView.builder.itemBuilder}
///
/// {@macro flutter.widgets.PageView.findChildIndexCallback}
///
/// The [gridDelegate] argument must not be null.
......@@ -1761,7 +1774,7 @@ class GridView extends BoxScrollView {
super.shrinkWrap,
super.padding,
required this.gridDelegate,
required IndexedWidgetBuilder itemBuilder,
required NullableIndexedWidgetBuilder itemBuilder,
ChildIndexGetter? findChildIndexCallback,
int? itemCount,
bool addAutomaticKeepAlives = true,
......
......@@ -143,6 +143,82 @@ void main() {
expect(textField.focusNode!.hasFocus, isFalse);
});
testWidgets('GridView.builder supports null items', (WidgetTester tester) async {
await tester.pumpWidget(textFieldBoilerplate(
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 42,
),
itemCount: 42,
itemBuilder: (BuildContext context, int index) {
if (index == 5) {
return null;
}
return const Text('item');
},
),
));
expect(find.text('item'), findsNWidgets(5));
});
testWidgets('ListView.builder supports null items', (WidgetTester tester) async {
await tester.pumpWidget(textFieldBoilerplate(
child: ListView.builder(
itemCount: 42,
itemBuilder: (BuildContext context, int index) {
if (index == 5) {
return null;
}
return const Text('item');
},
),
));
expect(find.text('item'), findsNWidgets(5));
});
testWidgets('PageView supports null items in itemBuilder', (WidgetTester tester) async {
await tester.pumpWidget(textFieldBoilerplate(
child: PageView.builder(
itemCount: 5,
controller: PageController(viewportFraction: 1/5),
itemBuilder: (BuildContext context, int index) {
if (index == 2) {
return null;
}
return const Text('item');
},
),
));
expect(find.text('item'), findsNWidgets(2));
});
testWidgets('ListView.separated supports null items in itemBuilder', (WidgetTester tester) async {
await tester.pumpWidget(textFieldBoilerplate(
child: ListView.separated(
itemCount: 42,
separatorBuilder: (BuildContext context, int index) {
return const Text('separator');
},
itemBuilder: (BuildContext context, int index) {
if (index == 5) {
return null;
}
return const Text('item');
},
),
));
expect(find.text('item'), findsNWidgets(5));
expect(find.text('separator'), findsNWidgets(5));
});
testWidgets('ListView.builder dismiss keyboard onDrag test', (WidgetTester tester) async {
final List<FocusNode> focusNodes = List<FocusNode>.generate(50, (int i) => FocusNode());
......
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