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
6bdc380b
Unverified
Commit
6bdc380b
authored
Apr 19, 2021
by
Mateus Felipe C. C. Pinto
Committed by
GitHub
Apr 19, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add prototypeItem property to ListView (#79752)
parent
ce18d702
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
138 additions
and
5 deletions
+138
-5
scroll_view.dart
packages/flutter/lib/src/widgets/scroll_view.dart
+61
-5
list_view_builder_test.dart
packages/flutter/test/widgets/list_view_builder_test.dart
+48
-0
scroll_view_test.dart
packages/flutter/test/widgets/scroll_view_test.dart
+29
-0
No files found.
packages/flutter/lib/src/widgets/scroll_view.dart
View file @
6bdc380b
...
...
@@ -21,6 +21,7 @@ import 'scroll_notification.dart';
import
'scroll_physics.dart'
;
import
'scrollable.dart'
;
import
'sliver.dart'
;
import
'sliver_prototype_extent_list.dart'
;
import
'viewport.dart'
;
// Examples can assume:
...
...
@@ -761,11 +762,19 @@ abstract class BoxScrollView extends ScrollView {
/// children are required to fill the [ListView].
///
/// If non-null, the [itemExtent] forces the children to have the given extent
/// in the scroll direction. Specifying an [itemExtent] is more efficient than
/// in the scroll direction.
///
/// If non-null, the [prototypeItem] forces the children to have the same extent
/// as the given widget in the scroll direction.
///
/// Specifying an [itemExtent] or an [prototypeItem] is more efficient than
/// letting the children determine their own extent because the scrolling
/// machinery can make use of the foreknowledge of the children's extent to save
/// work, for example when the scroll position changes drastically.
///
/// You can't specify both [itemExtent] and [prototypeItem], only one or none of
/// them.
///
/// There are four options for constructing a [ListView]:
///
/// 1. The default constructor takes an explicit [List<Widget>] of children. This
...
...
@@ -951,9 +960,10 @@ abstract class BoxScrollView extends ScrollView {
/// and [shrinkWrap] properties on [ListView] map directly to the identically
/// named properties on [CustomScrollView].
///
/// The [CustomScrollView.slivers] property should be a list containing either a
/// [SliverList] or a [SliverFixedExtentList]; the former if [itemExtent] on the
/// [ListView] was null, and the latter if [itemExtent] was not null.
/// The [CustomScrollView.slivers] property should be a list containing either:
/// * a [SliverList] if both [itemExtent] and [prototypeItem] were null;
/// * a [SliverFixedExtentList] if [itemExtent] was not null; or
/// * a [SliverPrototypeExtentList] if [prototypeItem] was not null.
///
/// The [childrenDelegate] property on [ListView] corresponds to the
/// [SliverList.delegate] (or [SliverFixedExtentList.delegate]) property. The
...
...
@@ -1104,6 +1114,7 @@ class ListView extends BoxScrollView {
bool
shrinkWrap
=
false
,
EdgeInsetsGeometry
?
padding
,
this
.
itemExtent
,
this
.
prototypeItem
,
bool
addAutomaticKeepAlives
=
true
,
bool
addRepaintBoundaries
=
true
,
bool
addSemanticIndexes
=
true
,
...
...
@@ -1114,7 +1125,11 @@ class ListView extends BoxScrollView {
ScrollViewKeyboardDismissBehavior
keyboardDismissBehavior
=
ScrollViewKeyboardDismissBehavior
.
manual
,
String
?
restorationId
,
Clip
clipBehavior
=
Clip
.
hardEdge
,
})
:
childrenDelegate
=
SliverChildListDelegate
(
})
:
assert
(
itemExtent
==
null
||
prototypeItem
==
null
,
'You can only pass itemExtent or prototypeItem, not both.'
,
),
childrenDelegate
=
SliverChildListDelegate
(
children
,
addAutomaticKeepAlives:
addAutomaticKeepAlives
,
addRepaintBoundaries:
addRepaintBoundaries
,
...
...
@@ -1178,6 +1193,7 @@ class ListView extends BoxScrollView {
bool
shrinkWrap
=
false
,
EdgeInsetsGeometry
?
padding
,
this
.
itemExtent
,
this
.
prototypeItem
,
required
IndexedWidgetBuilder
itemBuilder
,
int
?
itemCount
,
bool
addAutomaticKeepAlives
=
true
,
...
...
@@ -1191,6 +1207,10 @@ class ListView extends BoxScrollView {
Clip
clipBehavior
=
Clip
.
hardEdge
,
})
:
assert
(
itemCount
==
null
||
itemCount
>=
0
),
assert
(
semanticChildCount
==
null
||
semanticChildCount
<=
itemCount
!),
assert
(
itemExtent
==
null
||
prototypeItem
==
null
,
'You can only pass itemExtent or prototypeItem, not both.'
,
),
childrenDelegate
=
SliverChildBuilderDelegate
(
itemBuilder
,
childCount:
itemCount
,
...
...
@@ -1286,6 +1306,7 @@ class ListView extends BoxScrollView {
assert
(
separatorBuilder
!=
null
),
assert
(
itemCount
!=
null
&&
itemCount
>=
0
),
itemExtent
=
null
,
prototypeItem
=
null
,
childrenDelegate
=
SliverChildBuilderDelegate
(
(
BuildContext
context
,
int
index
)
{
final
int
itemIndex
=
index
~/
2
;
...
...
@@ -1425,6 +1446,7 @@ class ListView extends BoxScrollView {
bool
shrinkWrap
=
false
,
EdgeInsetsGeometry
?
padding
,
this
.
itemExtent
,
this
.
prototypeItem
,
required
this
.
childrenDelegate
,
double
?
cacheExtent
,
int
?
semanticChildCount
,
...
...
@@ -1433,6 +1455,10 @@ class ListView extends BoxScrollView {
String
?
restorationId
,
Clip
clipBehavior
=
Clip
.
hardEdge
,
})
:
assert
(
childrenDelegate
!=
null
),
assert
(
itemExtent
==
null
||
prototypeItem
==
null
,
'You can only pass itemExtent or prototypeItem, not both'
,
),
super
(
key:
key
,
scrollDirection:
scrollDirection
,
...
...
@@ -1457,8 +1483,33 @@ class ListView extends BoxScrollView {
/// determine their own extent because the scrolling machinery can make use of
/// the foreknowledge of the children's extent to save work, for example when
/// the scroll position changes drastically.
///
/// See also:
///
/// * [SliverFixedExtentList], the sliver used internally when this property
/// is provided. It constrains its box children to have a specific given
/// extent along the main axis.
/// * The [prototypeItem] property, which allows forcing the children's
/// extent to be the same as the given widget.
final
double
?
itemExtent
;
/// If non-null, forces the children to have the same extent as the given
/// widget in the scroll direction.
///
/// Specifying an [prototypeItem] is more efficient than letting the children
/// determine their own extent because the scrolling machinery can make use of
/// the foreknowledge of the children's extent to save work, for example when
/// the scroll position changes drastically.
///
/// See also:
///
/// * [SliverPrototypeExtentList], the sliver used internally when this
/// property is provided. It constrains its box children to have the same
/// extent as a prototype item along the main axis.
/// * The [itemExtent] property, which allows forcing the children's extent
/// to a given value.
final
Widget
?
prototypeItem
;
/// A delegate that provides the children for the [ListView].
///
/// The [ListView.custom] constructor lets you specify this delegate
...
...
@@ -1474,6 +1525,11 @@ class ListView extends BoxScrollView {
delegate:
childrenDelegate
,
itemExtent:
itemExtent
!,
);
}
else
if
(
prototypeItem
!=
null
)
{
return
SliverPrototypeExtentList
(
delegate:
childrenDelegate
,
prototypeItem:
prototypeItem
!,
);
}
return
SliverList
(
delegate:
childrenDelegate
);
}
...
...
packages/flutter/test/widgets/list_view_builder_test.dart
View file @
6bdc380b
...
...
@@ -261,6 +261,54 @@ void main() {
callbackTracker
.
clear
();
});
testWidgets
(
'ListView.builder 30 items with big jump, using prototypeItem'
,
(
WidgetTester
tester
)
async
{
final
List
<
int
>
callbackTracker
=
<
int
>[];
// The root view is 800x600 in the test environment and our list
// items are 300 tall. Scrolling should cause two or three items
// to be built.
Widget
itemBuilder
(
BuildContext
context
,
int
index
)
{
callbackTracker
.
add
(
index
);
return
Text
(
'
$index
'
,
key:
ValueKey
<
int
>(
index
),
textDirection:
TextDirection
.
ltr
);
}
final
Widget
testWidget
=
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
ListView
.
builder
(
itemBuilder:
itemBuilder
,
prototypeItem:
const
SizedBox
(
width:
800
,
height:
300
,
),
itemCount:
30
,
),
);
void
jumpTo
(
double
newScrollOffset
)
{
final
ScrollableState
scrollable
=
tester
.
state
(
find
.
byType
(
Scrollable
));
scrollable
.
position
.
jumpTo
(
newScrollOffset
);
}
await
tester
.
pumpWidget
(
testWidget
);
// 2 is in the cache area, but not visible.
expect
(
callbackTracker
,
equals
(<
int
>[
0
,
1
,
2
]));
final
List
<
int
>
initialExpectedHidden
=
List
<
int
>.
generate
(
28
,
(
int
i
)
=>
i
+
2
);
check
(
visible:
<
int
>[
0
,
1
],
hidden:
initialExpectedHidden
);
callbackTracker
.
clear
();
// Jump to the end of the ListView.
jumpTo
(
8400
);
await
tester
.
pump
();
// 27 is in the cache area, but not visible.
expect
(
callbackTracker
,
equals
(<
int
>[
27
,
28
,
29
]));
final
List
<
int
>
finalExpectedHidden
=
List
<
int
>.
generate
(
28
,
(
int
i
)
=>
i
);
check
(
visible:
<
int
>[
28
,
29
],
hidden:
finalExpectedHidden
);
callbackTracker
.
clear
();
});
testWidgets
(
'ListView.separated'
,
(
WidgetTester
tester
)
async
{
Widget
buildFrame
({
required
int
itemCount
})
{
return
Directionality
(
...
...
packages/flutter/test/widgets/scroll_view_test.dart
View file @
6bdc380b
...
...
@@ -1231,6 +1231,13 @@ void main() {
expect
(
finder
,
findsOneWidget
);
});
testWidgets
(
'ListView asserts on both non-null itemExtent and prototypeItem'
,
(
WidgetTester
tester
)
async
{
expect
(()
=>
ListView
(
itemExtent:
100
,
prototypeItem:
const
SizedBox
(),
),
throwsAssertionError
);
});
testWidgets
(
'ListView.builder asserts on negative childCount'
,
(
WidgetTester
tester
)
async
{
expect
(()
=>
ListView
.
builder
(
itemBuilder:
(
BuildContext
context
,
int
index
)
{
...
...
@@ -1260,6 +1267,28 @@ void main() {
),
throwsAssertionError
);
});
testWidgets
(
'ListView.builder asserts on both non-null itemExtent and prototypeItem'
,
(
WidgetTester
tester
)
async
{
expect
(()
=>
ListView
.
builder
(
itemBuilder:
(
BuildContext
context
,
int
index
)
{
return
const
SizedBox
();
},
itemExtent:
100
,
prototypeItem:
const
SizedBox
(),
),
throwsAssertionError
);
});
testWidgets
(
'ListView.custom asserts on both non-null itemExtent and prototypeItem'
,
(
WidgetTester
tester
)
async
{
expect
(()
=>
ListView
.
custom
(
childrenDelegate:
SliverChildBuilderDelegate
(
(
BuildContext
context
,
int
index
)
{
return
const
SizedBox
();
},
),
itemExtent:
100
,
prototypeItem:
const
SizedBox
(),
),
throwsAssertionError
);
});
testWidgets
(
'PrimaryScrollController provides fallback ScrollActions'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
...
...
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