Unverified Commit ae82f3cb authored by nt4f04uNd's avatar nt4f04uNd Committed by GitHub

Fix crash from invalid ListWheelViewport assertion (#126539)

Fixes https://github.com/flutter/flutter/issues/126491
parent 3ef9bc5f
......@@ -1067,18 +1067,6 @@ class RenderListWheelViewport
return result;
}
static bool _debugAssertValidPaintTransform(ListWheelParentData parentData) {
if (parentData.transform == null) {
throw FlutterError(
'Child paint transform happened to be null. \n'
'$RenderListWheelViewport normally paints all of the children it has laid out. \n'
'Did you forget to update the $ListWheelParentData.transform during the paint() call? \n'
'If this is intetional, change or remove this assertion accordingly.'
);
}
return true;
}
static bool _debugAssertValidHitTestOffsets(String context, Offset offset1, Offset offset2) {
if (offset1 != offset2) {
throw FlutterError("$context - hit test expected values didn't match: $offset1 != $offset2");
......@@ -1090,7 +1078,6 @@ class RenderListWheelViewport
void applyPaintTransform(RenderBox child, Matrix4 transform) {
final ListWheelParentData parentData = child.parentData! as ListWheelParentData;
final Matrix4? paintTransform = parentData.transform;
assert(_debugAssertValidPaintTransform(parentData));
if (paintTransform != null) {
transform.multiply(paintTransform);
}
......@@ -1110,15 +1097,13 @@ class RenderListWheelViewport
while (child != null) {
final ListWheelParentData childParentData = child.parentData! as ListWheelParentData;
final Matrix4? transform = childParentData.transform;
assert(_debugAssertValidPaintTransform(childParentData));
// Skip not painted children
if (transform != null) {
final bool isHit = result.addWithPaintTransform(
transform: transform,
position: position,
hitTest: (BoxHitTestResult result, Offset transformed) {
assert(() {
if (transform == null) {
return _debugAssertValidHitTestOffsets('Null transform', transformed, position);
}
final Matrix4? inverted = Matrix4.tryInvert(PointerEvent.removePerspectiveTransform(transform));
if (inverted == null) {
return _debugAssertValidHitTestOffsets('Null inverted transform', transformed, position);
......@@ -1131,6 +1116,7 @@ class RenderListWheelViewport
if (isHit) {
return true;
}
}
child = childParentData.previousSibling;
}
return false;
......
......@@ -10,6 +10,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart';
import '../rendering/rendering_tester.dart';
class SpyFixedExtentScrollController extends FixedExtentScrollController {
/// Override for test visibility only.
......@@ -528,4 +529,61 @@ void main() {
expect(controller.positions.length, 0);
});
testWidgets('Registers taps and does not crash with certain diameterRatio', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/126491
final List<int> children = List<int>.generate(100, (int index) => index);
final List<int> paintedChildren = <int>[];
final Set<int> tappedChildren = <int>{};
await tester.pumpWidget(CupertinoApp(
home: Align(
alignment: Alignment.topLeft,
child: Center(
child: SizedBox(
height: 120,
child: CupertinoPicker(
itemExtent: 55,
diameterRatio: 0.9,
onSelectedItemChanged: (int index) {},
children: children
.map<Widget>((int index) =>
GestureDetector(
key: ValueKey<int>(index),
onTap: () {
tappedChildren.add(index);
},
child: SizedBox(
width: 55,
height: 55,
child: CustomPaint(
painter: TestCallbackPainter(onPaint: () {
paintedChildren.add(index);
}),
),
),
),
)
.toList(),
),
),
),
),
));
// Children are painted two times for whatever reason
expect(paintedChildren, <int>[0, 1, 0, 1]);
// Expect hitting 0 and 1, which are painted
await tester.tap(find.byKey(const ValueKey<int>(0)));
expect(tappedChildren, const <int>[0]);
await tester.tap(find.byKey(const ValueKey<int>(1)));
expect(tappedChildren, const <int>[0, 1]);
// The third child is not painted, so is not hit
await tester.tap(find.byKey(const ValueKey<int>(2)), warnIfMissed: false);
expect(tappedChildren, const <int>[0, 1]);
});
}
......@@ -1642,6 +1642,65 @@ void main() {
expect(pageController.page, 1.0);
});
testWidgets('ListWheelScrollView does not crash and does not allow taps on children that were laid out, but not painted', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/12649
final FixedExtentScrollController controller = FixedExtentScrollController();
final List<int> children = List<int>.generate(100, (int index) => index);
final List<int> paintedChildren = <int>[];
final Set<int> tappedChildren = <int>{};
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: Center(
child: SizedBox(
height: 120,
child: ListWheelScrollView.useDelegate(
controller: controller,
physics: const FixedExtentScrollPhysics(),
diameterRatio: 0.9,
itemExtent: 55,
squeeze: 1.45,
childDelegate: ListWheelChildListDelegate(
children: children
.map((int index) => GestureDetector(
key: ValueKey<int>(index),
onTap: () {
tappedChildren.add(index);
},
child: SizedBox(
width: 55,
height: 55,
child: CustomPaint(
painter: TestCallbackPainter(onPaint: () {
paintedChildren.add(index);
}),
),
),
))
.toList(),
),
),
),
),
),
);
expect(paintedChildren, <int>[0, 1]);
// Expect hitting 0 and 1, which are painted
await tester.tap(find.byKey(const ValueKey<int>(0)));
expect(tappedChildren, const <int>[0]);
await tester.tap(find.byKey(const ValueKey<int>(1)));
expect(tappedChildren, const <int>[0, 1]);
// The third child is not painted, so is not hit
await tester.tap(find.byKey(const ValueKey<int>(2)), warnIfMissed: false);
expect(tappedChildren, const <int>[0, 1]);
});
});
testWidgets('ListWheelScrollView creates only one opacity layer for all children', (WidgetTester tester) async {
......
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