Unverified Commit aa8f8613 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Fix crash when disposing nested Scrollables while holding in overscroll position (#27864)

parent 964fab02
...@@ -53,10 +53,12 @@ abstract class Notification { ...@@ -53,10 +53,12 @@ abstract class Notification {
/// ///
/// The notification will be delivered to any [NotificationListener] widgets /// The notification will be delivered to any [NotificationListener] widgets
/// with the appropriate type parameters that are ancestors of the given /// with the appropriate type parameters that are ancestors of the given
/// [BuildContext]. /// [BuildContext]. If the [BuildContext] is null, the notification is not
/// dispatched.
void dispatch(BuildContext target) { void dispatch(BuildContext target) {
assert(target != null); // Only call dispatch if the widget's State is still mounted. // The `target` may be null if the subtree the notification is supposed to be
target.visitAncestorElements(visitAncestor); // dispatched in is in the process of being disposed.
target?.visitAncestorElements(visitAncestor);
} }
@override @override
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -28,4 +30,68 @@ void main() { ...@@ -28,4 +30,68 @@ void main() {
tester.state<FlipWidgetState>(find.byType(FlipWidget)).flip(); tester.state<FlipWidgetState>(find.byType(FlipWidget)).flip();
await tester.pump(const Duration(hours: 5)); await tester.pump(const Duration(hours: 5));
}); });
testWidgets('Disposing a (nested) Scrollable while holding in overscroll (iOS) does not crash', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/27707.
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
final ScrollController controller = ScrollController();
final Key outterContainer = GlobalKey();
await tester.pumpWidget(
MaterialApp(
home: Center(
child: Container(
key: outterContainer,
color: Colors.purple,
width: 400.0,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Container(
width: 500.0,
child: ListView.builder(
controller: controller,
itemBuilder: (BuildContext context, int index) {
return Container(
color: index % 2 == 0 ? Colors.red : Colors.green,
height: 200.0,
child: Text('Hello $index'),
);
},
),
),
),
),
),
),
);
// Go into overscroll.
double lastScrollOffset;
await tester.fling(find.text('Hello 0'), const Offset(0.0, 1000.0), 1000.0);
await tester.pump(const Duration(milliseconds: 100));
expect(lastScrollOffset = controller.offset, lessThan(0.0));
// Reduce the overscroll a little, but don't let it go back to 0.0.
await tester.pump(const Duration(milliseconds: 100));
expect(controller.offset, greaterThan(lastScrollOffset));
expect(controller.offset, lessThan(0.0));
final double currentOffset = controller.offset;
// Start a hold activity by putting one pointer down.
await tester.startGesture(tester.getTopLeft(find.byKey(outterContainer)) + const Offset(50.0, 50.0));
await tester.pumpAndSettle(); // This shouldn't change the scroll offset because of the down event above.
expect(controller.offset, currentOffset);
// Dispose the scrollables while the finger is still down, this should not crash.
await tester.pumpWidget(
MaterialApp(
home: Container(),
)
);
await tester.pumpAndSettle();
expect(controller.hasClients, isFalse);
debugDefaultTargetPlatformOverride = null;
});
} }
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