Fix crasher in DragableScrollableSheet when controller is animating and switching widgets (#125721)
We were failing to dispose of animation controllers created by `animateTo` when `didUpdateWidget` happens and we null out the `_attachedController`. This would cause the listener added here to fail on a null assertion:
fef41cfce0/packages/flutter/lib/src/widgets/draggable_scrollable_sheet.dart (L135)
Without the code change, the test ends up throwing exceptions related to that line. I don't have a great way to verify what this does visually though, hoping for help on that from internal customer.
b/279930163
See cl/527868355 as well.
Fixes https://github.com/flutter/flutter/issues/125709
This commit is contained in:
parent
9417b2a468
commit
e1e8ad8038
@ -206,6 +206,7 @@ class DraggableScrollableController extends ChangeNotifier {
|
||||
} else {
|
||||
_attachedController?.extent._currentSize.removeListener(notifyListeners);
|
||||
}
|
||||
_disposeAnimationControllers();
|
||||
_attachedController = null;
|
||||
}
|
||||
|
||||
|
@ -1404,22 +1404,21 @@ void main() {
|
||||
await tester.pumpWidget(boilerplate);
|
||||
expect(controller.isAttached, true);
|
||||
expect(controller.size, isNotNull);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('DraggableScrollableController.animateTo should not leak Ticker', (WidgetTester tester) async {
|
||||
// Regression test for https://github.com/flutter/flutter/issues/102483
|
||||
final DraggableScrollableController controller = DraggableScrollableController();
|
||||
await tester.pumpWidget(boilerplateWidget(() {}, controller: controller));
|
||||
testWidgets('DraggableScrollableController.animateTo after detach', (WidgetTester tester) async {
|
||||
final DraggableScrollableController controller = DraggableScrollableController();
|
||||
await tester.pumpWidget(boilerplateWidget(() {}, controller: controller));
|
||||
|
||||
controller.animateTo(0.0, curve: Curves.linear, duration: const Duration(milliseconds: 200));
|
||||
await tester.pump();
|
||||
controller.animateTo(0.0, curve: Curves.linear, duration: const Duration(milliseconds: 200));
|
||||
await tester.pump();
|
||||
|
||||
// Dispose the DraggableScrollableSheet
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
// Controller should be detached and no exception should be thrown
|
||||
expect(controller.isAttached, false);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
// Dispose the DraggableScrollableSheet
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
// Controller should be detached and no exception should be thrown
|
||||
expect(controller.isAttached, false);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('DraggableScrollableSheet should not reset programmatic drag on rebuild', (WidgetTester tester) async {
|
||||
// Regression test for https://github.com/flutter/flutter/issues/101114
|
||||
@ -1636,4 +1635,59 @@ void main() {
|
||||
controller2.jumpTo(1.0);
|
||||
expect(loggedSizes, <double>[1.0].map((double v) => closeTo(v, precisionErrorTolerance)));
|
||||
});
|
||||
|
||||
testWidgets('DraggableScrollableSheet controller can be changed while animating', (WidgetTester tester) async {
|
||||
final DraggableScrollableController controller1 = DraggableScrollableController();
|
||||
final DraggableScrollableController controller2 = DraggableScrollableController();
|
||||
|
||||
DraggableScrollableController controller = controller1;
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) => Scaffold(
|
||||
body: DraggableScrollableSheet(
|
||||
initialChildSize: 0.25,
|
||||
snap: true,
|
||||
snapSizes: const <double>[0.25, 0.5, 1.0],
|
||||
controller: controller,
|
||||
builder: (BuildContext context, ScrollController scrollController) {
|
||||
return ListView(
|
||||
controller: scrollController,
|
||||
children: <Widget>[
|
||||
ElevatedButton(
|
||||
onPressed: () => setState(() {
|
||||
controller = controller2;
|
||||
}),
|
||||
child: const Text('Switch controller'),
|
||||
),
|
||||
Container(
|
||||
height: 10000,
|
||||
color: Colors.blue,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
expect(controller1.isAttached, true);
|
||||
expect(controller2.isAttached, false);
|
||||
|
||||
|
||||
controller1.animateTo(0.5, curve: Curves.linear, duration: const Duration(milliseconds: 200));
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.text('Switch controller'));
|
||||
await tester.pump();
|
||||
|
||||
expect(controller1.isAttached, false);
|
||||
expect(controller2.isAttached, true);
|
||||
|
||||
controller2.animateTo(1.0, curve: Curves.linear, duration: const Duration(milliseconds: 200));
|
||||
await tester.pump();
|
||||
|
||||
await tester.pumpWidget(const SizedBox.shrink());
|
||||
expect(controller1.isAttached, false);
|
||||
expect(controller2.isAttached, false);
|
||||
});
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user