mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Added ListView.separated() constructor (#18619)
This commit is contained in:
parent
a57aff053e
commit
bec912165a
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
import 'basic.dart';
|
||||
@ -681,6 +683,91 @@ class ListView extends BoxScrollView {
|
||||
cacheExtent: cacheExtent
|
||||
);
|
||||
|
||||
/// Creates a fixed-length scrollable linear array of list "items" separated
|
||||
/// by list item "separators".
|
||||
///
|
||||
/// This constructor is appropriate for list views with a large number of
|
||||
/// item and separator children because the builders are only called for
|
||||
/// the children that are actually visible.
|
||||
///
|
||||
/// The `itemBuilder` callback will be called with indices greater than
|
||||
/// or equal to zero and less than `itemCount`.
|
||||
///
|
||||
/// Separators only appear between list items: separator 0 appears after item
|
||||
/// 0 and the last separator appears before the last item.
|
||||
///
|
||||
/// 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 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 [new ListView].
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// This example shows how to create [ListView] whose [ListTile] list items
|
||||
/// are separated by [Divider]s.
|
||||
///
|
||||
/// ```dart
|
||||
/// new ListView.separated(
|
||||
/// itemCount: 25,
|
||||
/// separatorBuilder: (BuildContext context, int index) => new Divider(),
|
||||
/// itemBuilder: (BuildContext context, int index) {
|
||||
/// return new ListTile(
|
||||
/// title: new Text('item $index'),
|
||||
/// );
|
||||
/// },
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// The `addAutomaticKeepAlives` argument corresponds to the
|
||||
/// [SliverChildBuilderDelegate.addAutomaticKeepAlives] property. The
|
||||
/// `addRepaintBoundaries` argument corresponds to the
|
||||
/// [SliverChildBuilderDelegate.addRepaintBoundaries] property. Both must not
|
||||
/// be null.
|
||||
ListView.separated({
|
||||
Key key,
|
||||
Axis scrollDirection = Axis.vertical,
|
||||
bool reverse = false,
|
||||
ScrollController controller,
|
||||
bool primary,
|
||||
ScrollPhysics physics,
|
||||
bool shrinkWrap = false,
|
||||
EdgeInsetsGeometry padding,
|
||||
@required IndexedWidgetBuilder itemBuilder,
|
||||
@required IndexedWidgetBuilder separatorBuilder,
|
||||
@required int itemCount,
|
||||
bool addAutomaticKeepAlives = true,
|
||||
bool addRepaintBoundaries = true,
|
||||
double cacheExtent,
|
||||
}) : assert(itemBuilder != null),
|
||||
assert(separatorBuilder != null),
|
||||
assert(itemCount != null && itemCount >= 0),
|
||||
itemExtent = null,
|
||||
childrenDelegate = new SliverChildBuilderDelegate(
|
||||
(BuildContext context, int index) {
|
||||
final int itemIndex = index ~/ 2;
|
||||
return (index == 0 || index.isEven)
|
||||
? itemBuilder(context, itemIndex)
|
||||
: separatorBuilder(context, itemIndex);
|
||||
},
|
||||
childCount: math.max(0, itemCount * 2 - 1),
|
||||
addAutomaticKeepAlives: addAutomaticKeepAlives,
|
||||
addRepaintBoundaries: addRepaintBoundaries,
|
||||
), super(
|
||||
key: key,
|
||||
scrollDirection: scrollDirection,
|
||||
reverse: reverse,
|
||||
controller: controller,
|
||||
primary: primary,
|
||||
physics: physics,
|
||||
shrinkWrap: shrinkWrap,
|
||||
padding: padding,
|
||||
cacheExtent: cacheExtent
|
||||
);
|
||||
|
||||
/// Creates a scrollable, linear array of widgets with a custom child model.
|
||||
///
|
||||
/// For example, a custom child model can control the algorithm used to
|
||||
|
||||
@ -261,6 +261,49 @@ void main() {
|
||||
callbackTracker.clear();
|
||||
});
|
||||
|
||||
testWidgets('ListView.separated', (WidgetTester tester) async {
|
||||
Widget buildFrame({ int itemCount }) {
|
||||
return new Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: new ListView.separated(
|
||||
itemCount: itemCount,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return new SizedBox(
|
||||
height: 100.0,
|
||||
child: new Text('i$index'),
|
||||
);
|
||||
},
|
||||
separatorBuilder: (BuildContext context, int index) {
|
||||
return new SizedBox(
|
||||
height: 10.0,
|
||||
child: new Text('s$index'),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await tester.pumpWidget(buildFrame(itemCount: 0));
|
||||
expect(find.text('i0'), findsNothing);
|
||||
expect(find.text('s0'), findsNothing);
|
||||
|
||||
await tester.pumpWidget(buildFrame(itemCount: 1));
|
||||
expect(find.text('i0'), findsOneWidget);
|
||||
expect(find.text('s0'), findsNothing);
|
||||
|
||||
await tester.pumpWidget(buildFrame(itemCount: 2));
|
||||
expect(find.text('i0'), findsOneWidget);
|
||||
expect(find.text('s0'), findsOneWidget);
|
||||
expect(find.text('i1'), findsOneWidget);
|
||||
expect(find.text('s1'), findsNothing);
|
||||
|
||||
// ListView's height is 600, so items i0-i5 and s0-s4 fit.
|
||||
await tester.pumpWidget(buildFrame(itemCount: 25));
|
||||
for(String s in <String>['i0', 's0', 'i1', 's1', 'i2', 's2', 'i3', 's3', 'i4', 's4', 'i5'])
|
||||
expect(find.text(s), findsOneWidget);
|
||||
expect(find.text('s5'), findsNothing);
|
||||
expect(find.text('i6'), findsNothing);
|
||||
});
|
||||
}
|
||||
|
||||
void check({List<int> visible = const <int>[], List<int> hidden = const <int>[]}) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user