Unverified Commit ff60778c authored by xubaolin's avatar xubaolin Committed by GitHub

[RenderListWheelViewport] Update content dimensions to prevent scroll offset changes (#96102)

parent 099aa029
......@@ -663,12 +663,10 @@ class RenderListWheelViewport
/// by [childManager].
@override
void performLayout() {
final BoxConstraints childConstraints =
constraints.copyWith(
minHeight: _itemExtent,
maxHeight: _itemExtent,
minWidth: 0.0,
);
// Apply the dimensions first in case it changes the scroll offset which
// determines what should be shown.
offset.applyViewportDimension(_viewportExtent);
offset.applyContentDimensions(_minEstimatedScrollExtent, _maxEstimatedScrollExtent);
// The height, in pixel, that children will be visible and might be laid out
// and painted.
......@@ -680,7 +678,7 @@ class RenderListWheelViewport
visibleHeight *= 2;
final double firstVisibleOffset =
offset.pixels + _itemExtent / 2 - visibleHeight / 2;
offset.pixels + _itemExtent / 2 - visibleHeight / 2;
final double lastVisibleOffset = firstVisibleOffset + visibleHeight;
// The index range that we want to spawn children. We find indexes that
......@@ -721,6 +719,11 @@ class RenderListWheelViewport
_destroyChild(firstChild!);
}
final BoxConstraints childConstraints = constraints.copyWith(
minHeight: _itemExtent,
maxHeight: _itemExtent,
minWidth: 0.0,
);
// If there is no child at this stage, we add the first one that is in
// target range.
if (childCount == 0) {
......@@ -759,8 +762,6 @@ class RenderListWheelViewport
_layoutChild(lastChild!, childConstraints, ++currentLastIndex);
}
offset.applyViewportDimension(_viewportExtent);
// Applying content dimensions bases on how the childManager builds widgets:
// if it is available to provide a child just out of target range, then
// we don't know whether there's a limit yet, and set the dimension to the
......@@ -770,16 +771,16 @@ class RenderListWheelViewport
? _minEstimatedScrollExtent
: indexToScrollOffset(targetFirstIndex);
final double maxScrollExtent = childManager.childExistsAt(targetLastIndex + 1)
? _maxEstimatedScrollExtent
: indexToScrollOffset(targetLastIndex);
? _maxEstimatedScrollExtent
: indexToScrollOffset(targetLastIndex);
offset.applyContentDimensions(minScrollExtent, maxScrollExtent);
}
bool _shouldClipAtCurrentOffset() {
final double highestUntransformedPaintY =
_getUntransformedPaintingCoordinateY(0.0);
_getUntransformedPaintingCoordinateY(0.0);
return highestUntransformedPaintY < 0.0
|| size.height < highestUntransformedPaintY + _maxEstimatedScrollExtent + _itemExtent;
|| size.height < highestUntransformedPaintY + _maxEstimatedScrollExtent + _itemExtent;
}
@override
......
......@@ -766,24 +766,26 @@ class _ListWheelScrollViewState extends State<ListWheelScrollView> {
}
}
bool _handleScrollNotification(ScrollNotification notification) {
if (notification.depth == 0
&& widget.onSelectedItemChanged != null
&& notification is ScrollUpdateNotification
&& notification.metrics is FixedExtentMetrics) {
final FixedExtentMetrics metrics = notification.metrics as FixedExtentMetrics;
final int currentItemIndex = metrics.itemIndex;
if (currentItemIndex != _lastReportedItemIndex) {
_lastReportedItemIndex = currentItemIndex;
final int trueIndex = widget.childDelegate.trueIndexOf(currentItemIndex);
widget.onSelectedItemChanged!(trueIndex);
}
}
return false;
}
@override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) {
if (notification.depth == 0
&& widget.onSelectedItemChanged != null
&& notification is ScrollUpdateNotification
&& notification.metrics is FixedExtentMetrics) {
final FixedExtentMetrics metrics = notification.metrics as FixedExtentMetrics;
final int currentItemIndex = metrics.itemIndex;
if (currentItemIndex != _lastReportedItemIndex) {
_lastReportedItemIndex = currentItemIndex;
final int trueIndex = widget.childDelegate.trueIndexOf(currentItemIndex);
widget.onSelectedItemChanged!(trueIndex);
}
}
return false;
},
onNotification: _handleScrollNotification,
child: _FixedExtentScrollable(
controller: scrollController,
physics: widget.physics,
......
......@@ -230,6 +230,7 @@ void main() {
});
testWidgets('child builder with lower and upper limits', (WidgetTester tester) async {
// Adjust the content dimensions at the end of `RenderListWheelViewport.performLayout()`
final List<int> paintedChildren = <int>[];
final FixedExtentScrollController controller =
......@@ -285,6 +286,69 @@ void main() {
});
group('layout', () {
// Regression test for https://github.com/flutter/flutter/issues/90953
testWidgets('ListWheelScrollView childDelegate update test 2', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController( initialItem: 2 );
Widget buildFrame(int childCount) {
return Directionality(
textDirection: TextDirection.ltr,
child: ListWheelScrollView.useDelegate(
controller: controller,
itemExtent: 400.0,
onSelectedItemChanged: (_) { },
childDelegate: ListWheelChildBuilderDelegate(
childCount: childCount,
builder: (BuildContext context, int index) {
return SizedBox(
width: 400.0,
height: 400.0,
child: Text(index.toString()),
);
},
),
),
);
}
await tester.pumpWidget(buildFrame(5));
expect(find.text('0'), findsNothing);
expect(tester.renderObject(find.text('1')).attached, true);
expect(tester.renderObject(find.text('2')).attached, true);
expect(tester.renderObject(find.text('3')).attached, true);
expect(find.text('4'), findsNothing);
// Remove the last 3 items.
await tester.pumpWidget(buildFrame(2));
expect(tester.renderObject(find.text('0')).attached, true);
expect(tester.renderObject(find.text('1')).attached, true);
expect(find.text('3'), findsNothing);
// Add 3 items at the end.
await tester.pumpWidget(buildFrame(5));
expect(tester.renderObject(find.text('0')).attached, true);
expect(tester.renderObject(find.text('1')).attached, true);
expect(tester.renderObject(find.text('2')).attached, true);
expect(find.text('3'), findsNothing);
expect(find.text('4'), findsNothing);
// Scroll to the last item.
final TestGesture scrollGesture = await tester.startGesture(const Offset(10.0, 10.0));
await scrollGesture.moveBy(const Offset(0.0, -1200.0));
await tester.pump();
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsNothing);
expect(find.text('2'), findsNothing);
expect(tester.renderObject(find.text('3')).attached, true);
expect(tester.renderObject(find.text('4')).attached, true);
// Remove the last 3 items.
await tester.pumpWidget(buildFrame(2));
expect(tester.renderObject(find.text('0')).attached, true);
expect(tester.renderObject(find.text('1')).attached, true);
expect(find.text('3'), findsNothing);
});
// Regression test for https://github.com/flutter/flutter/issues/58144
testWidgets('ListWheelScrollView childDelegate update test', (WidgetTester tester) async {
final FixedExtentScrollController controller = FixedExtentScrollController();
......
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