Your Location is: Home > Flutter

How can I test DropdownButton in Flutter with nullsafety? Crashing with <String?>

From: France View: 4010 snicz 

Question

When smoke testing a DropdownButton in Flutter using the flutter_test library I get the following error:

The following _TypeError was thrown while dispatching notifications for OverlayEntry: type 'Null' is not a subtype of type 'FutureOr<_DropdownRouteResult<String?>>'

followed by a stacktrace where some recursion is happening, repeating visitChildren and _unmount hundreds of times:

#1      TransitionRoute.dispose (package:flutter/src/widgets/routes.dart:420:26)
#2      _RouteEntry.dispose.<anonymous closure> (package:flutter/src/widgets/navigator.dart:3112:19)
#3      ChangeNotifier.notifyListeners (package:flutter/src/foundation/change_notifier.dart:243:25)
#4      OverlayEntry._updateMounted (package:flutter/src/widgets/overlay.dart:130:5)
#5      _OverlayEntryWidgetState.dispose (package:flutter/src/widgets/overlay.dart:200:18)
#6      StatefulElement.unmount (package:flutter/src/widgets/framework.dart:4911:11)
#7      _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2026:13)
#8      _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2024:7)
#9      MultiChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:6274:16)
#10     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2022:13)
#11     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2024:7)
#12     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:4739:14)
#13     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2022:13)
#14     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:2024:7)
...
#149    ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:4739:14)
#150    _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2022:13)
#150    ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:4739:14)
#151    _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:2022:13)
#161    ListIterable.forEach (dart:_internal/iterable.dart:39:13)
#162    _InactiveElements._unmountAll (package:flutter/src/widgets/framework.dart:2035:25)
#163    BuildOwner.finalizeTree.<anonymous closure> (package:flutter/src/widgets/framework.dart:2869:27)
#164    BuildOwner.lockState (package:flutter/src/widgets/framework.dart:2669:15)
#165    BuildOwner.finalizeTree (package:flutter/src/widgets/framework.dart:2868:7)
#166    AutomatedTestWidgetsFlutterBinding.drawFrame (package:flutter_test/src/binding.dart:1130:19)
#167    RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:320:5)
#168    SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1119:15)
#169    SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1057:9)
#170    AutomatedTestWidgetsFlutterBinding.scheduleWarmUpFrame (package:flutter_test/src/binding.dart:1056:5)
#171    runApp (package:flutter/src/widgets/binding.dart:1049:7)
#172    TestWidgetsFlutterBinding._runTestBody (package:flutter_test/src/binding.dart:804:7)

The error only occurs while testing and the app runs on a physical device without any errors.

main.dart

Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: DropdownButton<String?>(
          key: const Key('dropdownbutton'),
          hint: const Text('ASD'),
          items: const [
            DropdownMenuItem<String>(value: '1', child: Text('1')),
            DropdownMenuItem<String>(value: '2', child: Text('2')),
          ],
          onChanged: (_) {},
        ),
      ),
    );
  }

widget_test.dart

final Type dropdownMenuItemType =
    DropdownMenuItem<String?>(child: Container()).runtimeType;

void main() {
  testWidgets('Tap DropdownButton makes DropdownMenuItems visible',
      (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MyApp());

    // expect(find.byType(dropdownMenuItemType).hitTestable(), findsNothing);

    //open dropdown menu
    await tester.tap(find.byKey(const Key('dropdownbutton')));
    await tester.pump();
    await tester.pump(const Duration(seconds: 1));

    // expect(find.text('1').hitTestable(), findsOneWidget);
    // expect(find.text('2').hitTestable(), findsOneWidget);
  });
}

The error can be 'fixed' by setting DropdownButton<String?> in main.dart to DropdownButton<String>. I do understand that null can not be a value for a DropDownButton and therefore the "?" can just be removed or it will crash on null anyway. But I do not understand why it crashes when the data is sufficient.

Minimal code example here.

Best answer