Adam Barth 6399a3af41 Fix DropdownButton regression (#6353)
When I changed how routes complete their futures, I broke the Dropdown
button because it was still waiting for its own Completer to complete
instead of using the Future returned by push. This patch fixes that
issue.

I've also removed the previous behavior of the DropdownButton forwarding
its text style to its route. The mechansim that we were using doesn't
work properly in all cases. For example, if the DropdownButton is a
child of a LayoutBuilder, then the route will have already built by the
time the DropdownButton gets a chance to forward its text style, causing
an assert in setState.

Finally, I've tweaked PopupMenuButton to work the same way as
DropdownButton in a couple corner cases (e.g., not calling the changed
callback if the button was removed from the tree before the menu
completed its Future).

Fixes #6352
2016-10-17 10:00:48 -07:00

119 lines
3.4 KiB
Dart

// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart';
void main() {
testWidgets('Drop down button control test', (WidgetTester tester) async {
List<String> items = <String>['one', 'two', 'three', 'four'];
String value = items.first;
void didChangeValue(String newValue) {
value = newValue;
}
Widget build() {
return new MaterialApp(
home: new Material(
child: new Center(
child: new DropdownButton<String>(
value: value,
items: items.map((String item) {
return new DropdownMenuItem<String>(
value: item,
child: new Text(item),
);
}).toList(),
onChanged: didChangeValue,
),
),
),
);
}
await tester.pumpWidget(build());
await tester.tap(find.text('one'));
await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the menu animation
expect(value, equals('one'));
await tester.tap(find.text('three').last);
await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the menu animation
expect(value, equals('three'));
await tester.tap(find.text('three'));
await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the menu animation
expect(value, equals('three'));
await tester.pumpWidget(build());
await tester.tap(find.text('two').last);
await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the menu animation
expect(value, equals('two'));
});
testWidgets('Drop down screen edges', (WidgetTester tester) async {
int value = 4;
List<DropdownMenuItem<int>> items = <DropdownMenuItem<int>>[];
for (int i = 0; i < 20; ++i)
items.add(new DropdownMenuItem<int>(value: i, child: new Text('$i')));
void handleChanged(int newValue) {
value = newValue;
}
DropdownButton<int> button = new DropdownButton<int>(
value: value,
onChanged: handleChanged,
items: items,
);
await tester.pumpWidget(
new MaterialApp(
home: new Material(
child: new Align(
alignment: FractionalOffset.topCenter,
child: button,
),
),
),
);
await tester.tap(find.text('4'));
await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the menu animation
// We should have two copies of item 5, one in the menu and one in the
// button itself.
expect(tester.elementList(find.text('5')), hasLength(2));
// We should only have one copy of item 19, which is in the button itself.
// The copy in the menu shouldn't be in the tree because it's off-screen.
expect(tester.elementList(find.text('19')), hasLength(1));
expect(value, 4);
await tester.tap(find.byConfig(button));
expect(value, 4);
// this waits for the route's completer to complete, which calls handleChanged
await tester.idle();
expect(value, 4);
// TODO(abarth): Remove these calls to pump once navigator cleans up its
// pop transitions.
await tester.pump();
await tester.pump(const Duration(seconds: 1)); // finish the menu animation
});
}