I'm trying to write widget tests for a DropdownButton in my app. I noticed that after tapping the button to open it the call to find.byType(DropdownMenuItem)
is returning double the expected number of DropdownMenuItems.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
enum MyColor {
blue,
green,
red,
yellow,
black,
pink
}
Future<void> main() async {
// runApp(MyApp());
// tests
group('dropdown tests', () {
testWidgets('how many elements should be found?', (tester) async {
await tester.pumpWidget(MyApp());
await tester.pumpAndSettle();
expect(find.byType(DropdownButton<MyColor>), findsOneWidget);
await tester.tap(find.byType(DropdownButton<MyColor>));
await tester.pumpAndSettle();
// fails
// expect(find.byType(DropdownMenuItem<MyColor>), findsNWidgets(MyColor.values.length));
// passes
expect(find.byType(DropdownMenuItem<MyColor>), findsNWidgets(MyColor.values.length * 2));
});
});
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
@override
State<StatefulWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
MyColor selected = MyColor.blue;
@override
Widget build(BuildContext context) {
return DropdownButton<MyColor>(
value: selected,
items: MyColor.values.map((col) {
return DropdownMenuItem<MyColor>(
child: Text(col.name),
value: col,
);
}).toList(),
onChanged: (value) {
if (value == null) {
return;
}
print('${value.name} selected');
setState(() {
selected = value;
});
}
);
}
}
Dartpad: https://dartpad.dev/?id=ce3eadff6bd98e6005817c70883451a0
I suspect that this has something to do with how Flutter renders the scene. I looked into the widget tests for the dropdown in the Flutter repo but I don't see any difference between my setup and theirs, but I also don't see any calls to find.byType(DropdownMenuItem)
. Does anyone know why this happens? Or is there an error in my code?
When an DropdownButton
is rendered initially all items are rendered with IndexedStack
and based on the selected value we see one visible item at the top
find.byType(DropdownMenuItem<MyColor>)
will find 6
itemsOnce you tap on DropdownButton
a _DropdownRoute
route is pushed with all the items
find.byType(DropdownMenuItem<MyColor>)
will find 12
items (the first 6 items are from IndexedStack
and the second 6
items are from the new route)
So the number of items should be double at this stage as documented in the flutter tests as well// Each item appears twice, once in the menu and once // in the dropdown button's IndexedStack.
Once you tap on of the DropdownMenuItem
items the number of found widgets will go back to 6