From 213b3cb3d8121c0687607baa307eb32580e2dacc Mon Sep 17 00:00:00 2001 From: Mingyu Date: Thu, 19 Jan 2023 21:35:34 +0800 Subject: [PATCH] Check whether slider is mounted before interaction, no-op if unmounted (#113556) * Check whether slider is unmounted before interaction * Update slider.dart * Update Slider * Add test * Update slider_test.dart * Update packages/flutter/test/material/slider_test.dart Co-authored-by: Taha Tesser Co-authored-by: Taha Tesser --- packages/flutter/lib/src/material/slider.dart | 3 + .../flutter/test/material/slider_test.dart | 55 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/packages/flutter/lib/src/material/slider.dart b/packages/flutter/lib/src/material/slider.dart index 831279b85a66..7c9ab993f762 100644 --- a/packages/flutter/lib/src/material/slider.dart +++ b/packages/flutter/lib/src/material/slider.dart @@ -1528,6 +1528,9 @@ class _RenderSlider extends RenderBox with RelayoutWhenSystemFontsChangeMixin { @override void handleEvent(PointerEvent event, BoxHitTestEntry entry) { + if (!_state.mounted) { + return; + } assert(debugHandleEvent(event, entry)); if (event is PointerDownEvent && isInteractive) { // We need to add the drag first so that it has priority. diff --git a/packages/flutter/test/material/slider_test.dart b/packages/flutter/test/material/slider_test.dart index 84e55936f12a..f94044c9e8d2 100644 --- a/packages/flutter/test/material/slider_test.dart +++ b/packages/flutter/test/material/slider_test.dart @@ -3437,6 +3437,61 @@ void main() { ); }, variant: TargetPlatformVariant.desktop()); + testWidgets('Event on Slider should perform no-op if already unmounted', (WidgetTester tester) async { + // Test covering crashing found in Google internal issue b/192329942. + double value = 0.0; + final ValueNotifier shouldShowSliderListenable = + ValueNotifier(true); + + await tester.pumpWidget( + MaterialApp( + home: Directionality( + textDirection: TextDirection.ltr, + child: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return Material( + child: Center( + child: ValueListenableBuilder( + valueListenable: shouldShowSliderListenable, + builder: (BuildContext context, bool shouldShowSlider, _) { + return shouldShowSlider + ? Slider( + value: value, + onChanged: (double newValue) { + setState(() { + value = newValue; + }); + }, + ) + : const SizedBox.shrink(); + }, + ), + ), + ); + }, + ), + ), + ), + ); + + final TestGesture gesture = await tester + .startGesture(tester.getRect(find.byType(Slider)).centerLeft); + + // Intentioanlly not calling `await tester.pumpAndSettle()` to allow drag + // event performed on `Slider` before it is about to get unmounted. + shouldShowSliderListenable.value = false; + + await tester.drag(find.byType(Slider), const Offset(1.0, 0.0)); + await tester.pumpAndSettle(); + + expect(value, equals(0.0)); + + // This is supposed to trigger animation on `Slider` if it is mounted. + await gesture.up(); + + expect(tester.takeException(), null); + }); + group('Material 2', () { // Tests that are only relevant for Material 2. Once ThemeData.useMaterial3 // is turned on by default, these tests can be removed.