1

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.

snicz
  • 61
  • 7
  • there is an open issue on github [here](https://github.com/flutter/flutter/issues/74389) – snicz Jan 22 '21 at 07:35
  • Quoting [shihaohong](https://github.com/shihaohong): `The problem is that everything that relies on on T like value and ValueChanged handles a nullable version of T by default, because a lot of the inner workings of DropdownButton have implicit behavior for when T is nullable. I also find it odd that it runs fine on an app and only fails in a test, but am not sure why this is the case.` [github response](https://github.com/flutter/flutter/issues/74389#issuecomment-766866061). – snicz Jan 26 '21 at 09:48

1 Answers1

0

The issue seems to have been caused by a bug in Dart. The fix was also rolled out into Flutter later on and has confirmed to be working. Updating to the latest version of Flutter SDK should solve the issue.

Omatt
  • 8,564
  • 2
  • 42
  • 144