Unverified Commit b6d13a61 authored by Viren Khatri's avatar Viren Khatri Committed by GitHub

Adding `itemExtent` to `ReorderableList` and `ReorderableListView` (#81372)

parent d97f41ca
...@@ -76,6 +76,7 @@ class ReorderableListView extends StatefulWidget { ...@@ -76,6 +76,7 @@ class ReorderableListView extends StatefulWidget {
Key? key, Key? key,
required List<Widget> children, required List<Widget> children,
required this.onReorder, required this.onReorder,
this.itemExtent,
this.proxyDecorator, this.proxyDecorator,
this.buildDefaultDragHandles = true, this.buildDefaultDragHandles = true,
this.padding, this.padding,
...@@ -168,6 +169,7 @@ class ReorderableListView extends StatefulWidget { ...@@ -168,6 +169,7 @@ class ReorderableListView extends StatefulWidget {
required this.itemBuilder, required this.itemBuilder,
required this.itemCount, required this.itemCount,
required this.onReorder, required this.onReorder,
this.itemExtent,
this.proxyDecorator, this.proxyDecorator,
this.buildDefaultDragHandles = true, this.buildDefaultDragHandles = true,
this.padding, this.padding,
...@@ -323,6 +325,9 @@ class ReorderableListView extends StatefulWidget { ...@@ -323,6 +325,9 @@ class ReorderableListView extends StatefulWidget {
/// Defaults to [Clip.hardEdge]. /// Defaults to [Clip.hardEdge].
final Clip clipBehavior; final Clip clipBehavior;
/// {@macro flutter.widgets.list_view.itemExtent}
final double? itemExtent;
@override @override
_ReorderableListViewState createState() => _ReorderableListViewState(); _ReorderableListViewState createState() => _ReorderableListViewState();
} }
...@@ -545,6 +550,7 @@ class _ReorderableListViewState extends State<ReorderableListView> { ...@@ -545,6 +550,7 @@ class _ReorderableListViewState extends State<ReorderableListView> {
padding: listPadding, padding: listPadding,
sliver: SliverReorderableList( sliver: SliverReorderableList(
itemBuilder: _itemBuilder, itemBuilder: _itemBuilder,
itemExtent: widget.itemExtent,
itemCount: widget.itemCount, itemCount: widget.itemCount,
onReorder: widget.onReorder, onReorder: widget.onReorder,
proxyDecorator: widget.proxyDecorator ?? _proxyDecorator, proxyDecorator: widget.proxyDecorator ?? _proxyDecorator,
......
...@@ -112,6 +112,7 @@ class ReorderableList extends StatefulWidget { ...@@ -112,6 +112,7 @@ class ReorderableList extends StatefulWidget {
required this.itemBuilder, required this.itemBuilder,
required this.itemCount, required this.itemCount,
required this.onReorder, required this.onReorder,
this.itemExtent,
this.proxyDecorator, this.proxyDecorator,
this.padding, this.padding,
this.scrollDirection = Axis.vertical, this.scrollDirection = Axis.vertical,
...@@ -211,6 +212,9 @@ class ReorderableList extends StatefulWidget { ...@@ -211,6 +212,9 @@ class ReorderableList extends StatefulWidget {
/// Defaults to [Clip.hardEdge]. /// Defaults to [Clip.hardEdge].
final Clip clipBehavior; final Clip clipBehavior;
/// {@macro flutter.widgets.list_view.itemExtent}
final double? itemExtent;
/// The state from the closest instance of this class that encloses the given /// The state from the closest instance of this class that encloses the given
/// context. /// context.
/// ///
...@@ -337,6 +341,7 @@ class ReorderableListState extends State<ReorderableList> { ...@@ -337,6 +341,7 @@ class ReorderableListState extends State<ReorderableList> {
padding: widget.padding ?? EdgeInsets.zero, padding: widget.padding ?? EdgeInsets.zero,
sliver: SliverReorderableList( sliver: SliverReorderableList(
key: _sliverReorderableListKey, key: _sliverReorderableListKey,
itemExtent: widget.itemExtent,
itemBuilder: widget.itemBuilder, itemBuilder: widget.itemBuilder,
itemCount: widget.itemCount, itemCount: widget.itemCount,
onReorder: widget.onReorder, onReorder: widget.onReorder,
...@@ -380,6 +385,7 @@ class SliverReorderableList extends StatefulWidget { ...@@ -380,6 +385,7 @@ class SliverReorderableList extends StatefulWidget {
required this.itemBuilder, required this.itemBuilder,
required this.itemCount, required this.itemCount,
required this.onReorder, required this.onReorder,
this.itemExtent,
this.proxyDecorator, this.proxyDecorator,
}) : assert(itemCount >= 0), }) : assert(itemCount >= 0),
super(key: key); super(key: key);
...@@ -396,6 +402,9 @@ class SliverReorderableList extends StatefulWidget { ...@@ -396,6 +402,9 @@ class SliverReorderableList extends StatefulWidget {
/// {@macro flutter.widgets.reorderable_list.proxyDecorator} /// {@macro flutter.widgets.reorderable_list.proxyDecorator}
final ReorderItemProxyDecorator? proxyDecorator; final ReorderItemProxyDecorator? proxyDecorator;
/// {@macro flutter.widgets.list_view.itemExtent}
final double? itemExtent;
@override @override
SliverReorderableListState createState() => SliverReorderableListState(); SliverReorderableListState createState() => SliverReorderableListState();
...@@ -825,15 +834,19 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke ...@@ -825,15 +834,19 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasOverlay(context)); assert(debugCheckHasOverlay(context));
return SliverList( final SliverChildBuilderDelegate childrenDelegate = SliverChildBuilderDelegate(
_itemBuilder,
// When dragging, the dragged item is still in the list but has been replaced // When dragging, the dragged item is still in the list but has been replaced
// by a zero height SizedBox, so that the gap can move around. To make the // by a zero height SizedBox, so that the gap can move around. To make the
// list extent stable we add a dummy entry to the end. // list extent stable we add a dummy entry to the end.
delegate: SliverChildBuilderDelegate( childCount: widget.itemCount + (_dragInfo != null ? 1 : 0),
_itemBuilder,
childCount: widget.itemCount + (_dragInfo != null ? 1 : 0),
),
); );
return widget.itemExtent != null
? SliverFixedExtentList(
itemExtent: widget.itemExtent!,
delegate: childrenDelegate,
)
: SliverList(delegate: childrenDelegate);
} }
} }
......
...@@ -1476,6 +1476,7 @@ class ListView extends BoxScrollView { ...@@ -1476,6 +1476,7 @@ class ListView extends BoxScrollView {
clipBehavior: clipBehavior, clipBehavior: clipBehavior,
); );
/// {@template flutter.widgets.list_view.itemExtent}
/// If non-null, forces the children to have the given extent in the scroll /// If non-null, forces the children to have the given extent in the scroll
/// direction. /// direction.
/// ///
...@@ -1489,6 +1490,7 @@ class ListView extends BoxScrollView { ...@@ -1489,6 +1490,7 @@ class ListView extends BoxScrollView {
/// * [SliverFixedExtentList], the sliver used internally when this property /// * [SliverFixedExtentList], the sliver used internally when this property
/// is provided. It constrains its box children to have a specific given /// is provided. It constrains its box children to have a specific given
/// extent along the main axis. /// extent along the main axis.
/// {@endtemplate}
/// * The [prototypeItem] property, which allows forcing the children's /// * The [prototypeItem] property, which allows forcing the children's
/// extent to be the same as the given widget. /// extent to be the same as the given widget.
final double? itemExtent; final double? itemExtent;
......
...@@ -1505,6 +1505,51 @@ void main() { ...@@ -1505,6 +1505,51 @@ void main() {
expect(exception.toString(), contains('No Overlay widget found')); expect(exception.toString(), contains('No Overlay widget found'));
expect(exception.toString(), contains('ReorderableListView widgets require an Overlay widget ancestor')); expect(exception.toString(), contains('ReorderableListView widgets require an Overlay widget ancestor'));
}); });
testWidgets('if itemExtent is non-null, children have same extent in the scroll direction', (WidgetTester tester) async {
final List<int> numbers = <int>[0,1,2];
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return ReorderableListView.builder(
itemBuilder: (BuildContext context, int index) {
return SizedBox(
key: ValueKey<int>(numbers[index]),
// children with different heights
height: 20 + numbers[index] * 10,
child: ReorderableDragStartListener(
index: index,
child: Text(numbers[index].toString()),
)
);
},
itemCount: numbers.length,
itemExtent: 30,
onReorder: (int fromIndex, int toIndex) {
if (fromIndex < toIndex) {
toIndex--;
}
final int value = numbers.removeAt(fromIndex);
numbers.insert(toIndex, value);
},
);
},
),
),
)
);
final double item0Height = tester.getSize(find.text('0').hitTestable()).height;
final double item1Height = tester.getSize(find.text('1').hitTestable()).height;
final double item2Height = tester.getSize(find.text('2').hitTestable()).height;
expect(item0Height, 30.0);
expect(item1Height, 30.0);
expect(item2Height, 30.0);
});
} }
Future<void> longPressDrag(WidgetTester tester, Offset start, Offset end) async { Future<void> longPressDrag(WidgetTester tester, Offset start, Offset end) async {
......
...@@ -267,6 +267,51 @@ void main() { ...@@ -267,6 +267,51 @@ void main() {
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(getItemFadeTransition(), findsNothing); expect(getItemFadeTransition(), findsNothing);
}); });
testWidgets('if itemExtent is non-null, children have same extent in the scroll direction', (WidgetTester tester) async {
final List<int> numbers = <int>[0,1,2];
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return ReorderableList(
itemBuilder: (BuildContext context, int index) {
return SizedBox(
key: ValueKey<int>(numbers[index]),
// children with different heights
height: 20 + numbers[index] * 10,
child: ReorderableDragStartListener(
index: index,
child: Text(numbers[index].toString()),
)
);
},
itemCount: numbers.length,
itemExtent: 30,
onReorder: (int fromIndex, int toIndex) {
if (fromIndex < toIndex) {
toIndex--;
}
final int value = numbers.removeAt(fromIndex);
numbers.insert(toIndex, value);
},
);
},
),
),
)
);
final double item0Height = tester.getSize(find.text('0').hitTestable()).height;
final double item1Height = tester.getSize(find.text('1').hitTestable()).height;
final double item2Height = tester.getSize(find.text('2').hitTestable()).height;
expect(item0Height, 30.0);
expect(item1Height, 30.0);
expect(item2Height, 30.0);
});
} }
class TestList extends StatefulWidget { class TestList extends StatefulWidget {
......
...@@ -1334,4 +1334,42 @@ void main() { ...@@ -1334,4 +1334,42 @@ void main() {
equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)),
); );
}); });
testWidgets('if itemExtent is non-null, children have same extent in the scroll direction', (WidgetTester tester) async {
final List<int> numbers = <int>[0,1,2];
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return ListView.builder(
itemBuilder: (BuildContext context, int index) {
return SizedBox(
key: ValueKey<int>(numbers[index]),
// children with different heights
height: 20 + numbers[index] * 10,
child: ReorderableDragStartListener(
index: index,
child: Text(numbers[index].toString()),
)
);
},
itemCount: numbers.length,
itemExtent: 30,
);
},
),
),
)
);
final double item0Height = tester.getSize(find.text('0').hitTestable()).height;
final double item1Height = tester.getSize(find.text('1').hitTestable()).height;
final double item2Height = tester.getSize(find.text('2').hitTestable()).height;
expect(item0Height, 30.0);
expect(item1Height, 30.0);
expect(item2Height, 30.0);
});
} }
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