diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index 877ab8972ff..ef509e28156 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -456,7 +456,9 @@ class _DropdownRoute extends PopupRoute<_DropdownRouteResult> { } void _dismiss() { - navigator?.removeRoute(this); + if (isActive) { + navigator?.removeRoute(this); + } } double getItemOffset(int index) { diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart index 8bb9c2ef68d..8151edbe04e 100644 --- a/packages/flutter/test/material/dropdown_test.dart +++ b/packages/flutter/test/material/dropdown_test.dart @@ -2615,4 +2615,52 @@ void main() { expect(value, equals('two')); expect(menuItemTapCounters, [0, 2, 1, 0]); }); + + testWidgets('does not crash when option is selected without waiting for opening animation to complete', (WidgetTester tester) async { + // Regression test for b/171846624. + + final List options = ['first', 'second', 'third']; + String? value = options.first; + + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) => DropdownButton( + value: value, + items: options.map((String s) => DropdownMenuItem( + value: s, + child: Text(s), + )).toList(), + onChanged: (String? v) { + setState(() { + value = v; + }); + }, + ), + ), + ), + ), + ); + expect(find.text('first').hitTestable(), findsOneWidget); + expect(find.text('second').hitTestable(), findsNothing); + expect(find.text('third').hitTestable(), findsNothing); + + // Open dropdown. + await tester.tap(find.text('first').hitTestable()); + await tester.pump(); + + expect(find.text('third').hitTestable(), findsOneWidget); + expect(find.text('first').hitTestable(), findsOneWidget); + expect(find.text('second').hitTestable(), findsOneWidget); + + // Deliberately not waiting for opening animation to complete! + + // Select an option in dropdown. + await tester.tap(find.text('third').hitTestable()); + await tester.pump(); + expect(find.text('third').hitTestable(), findsOneWidget); + expect(find.text('first').hitTestable(), findsNothing); + expect(find.text('second').hitTestable(), findsNothing); + }); }