Expand DropdownButton width based on isExpanded flag (#21515)
* Fix Dropdown where icon not set to end of button
* Expand DropdownButton based on isExpanded flag
* Add dropdown golden tests
Fixes #13135, #16606
diff --git a/bin/internal/goldens.version b/bin/internal/goldens.version
index 4971511..5d80160 100644
--- a/bin/internal/goldens.version
+++ b/bin/internal/goldens.version
@@ -1 +1 @@
-555a2f96848c31b8c0303555535d6b5e20686e6e
+5b0696a651738116b4d25a5b0a946b0951e45e6f
diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart
index a710bca..043d985 100644
--- a/packages/flutter/lib/src/material/dropdown.dart
+++ b/packages/flutter/lib/src/material/dropdown.dart
@@ -484,6 +484,7 @@
this.style,
this.iconSize = 24.0,
this.isDense = false,
+ this.isExpanded = false,
}) : assert(items != null),
assert(value == null || items.where((DropdownMenuItem<T> item) => item.value == value).length == 1),
super(key: key);
@@ -529,6 +530,13 @@
/// its own decorations, like [InputDecorator].
final bool isDense;
+ /// Set the dropdown's inner contents to horizontally fill its parent.
+ ///
+ /// By default this button's inner width is the minimum size of its contents.
+ /// If [isExpanded] is true, the inner width is expanded to fill its
+ /// surrounding container.
+ final bool isExpanded;
+
@override
_DropdownButtonState<T> createState() => new _DropdownButtonState<T>();
}
@@ -643,6 +651,14 @@
? _kAlignedButtonPadding
: _kUnalignedButtonPadding;
+ // If value is null (then _selectedIndex is null) then we display
+ // the hint or nothing at all.
+ final IndexedStack innerItemsWidget = new IndexedStack(
+ index: _selectedIndex ?? hintIndex,
+ alignment: AlignmentDirectional.centerStart,
+ children: items,
+ );
+
Widget result = new DefaultTextStyle(
style: _textStyle,
child: new Container(
@@ -652,13 +668,7 @@
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
- // If value is null (then _selectedIndex is null) then we display
- // the hint or nothing at all.
- new IndexedStack(
- index: _selectedIndex ?? hintIndex,
- alignment: AlignmentDirectional.centerStart,
- children: items,
- ),
+ widget.isExpanded ? new Expanded(child: innerItemsWidget) : innerItemsWidget,
new Icon(Icons.arrow_drop_down,
size: widget.iconSize,
// These colors are not defined in the Material Design spec.
diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart
index 0693687..118e288 100644
--- a/packages/flutter/test/material/dropdown_test.dart
+++ b/packages/flutter/test/material/dropdown_test.dart
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'dart:io';
import 'dart:math' as math;
import 'dart:ui' show window;
@@ -23,6 +24,7 @@
String value = 'two',
ValueChanged<String> onChanged,
bool isDense = false,
+ bool isExpanded = false,
Widget hint,
List<String> items = menuItems,
Alignment alignment = Alignment.center,
@@ -33,19 +35,22 @@
child: new Material(
child: new Align(
alignment: alignment,
- child: new DropdownButton<String>(
- key: buttonKey,
- value: value,
- hint: hint,
- onChanged: onChanged,
- isDense: isDense,
- items: items.map((String item) {
- return new DropdownMenuItem<String>(
- key: new ValueKey<String>(item),
- value: item,
- child: new Text(item, key: new ValueKey<String>(item + 'Text')),
- );
- }).toList(),
+ child: new RepaintBoundary(
+ child: new DropdownButton<String>(
+ key: buttonKey,
+ value: value,
+ hint: hint,
+ onChanged: onChanged,
+ isDense: isDense,
+ isExpanded: isExpanded,
+ items: items.map((String item) {
+ return new DropdownMenuItem<String>(
+ key: new ValueKey<String>(item),
+ value: item,
+ child: new Text(item, key: new ValueKey<String>(item + 'Text')),
+ );
+ }).toList(),
+ )
),
),
),
@@ -108,6 +113,32 @@
}
void main() {
+ testWidgets('Default dropdown golden', (WidgetTester tester) async {
+ final Key buttonKey = new UniqueKey();
+ Widget build() => buildFrame(buttonKey: buttonKey, value: 'two');
+ await tester.pumpWidget(build());
+ final Finder buttonFinder = find.byKey(buttonKey);
+ assert(tester.renderObject(buttonFinder).attached);
+ await expectLater(
+ find.ancestor(of: buttonFinder, matching: find.byType(RepaintBoundary)).first,
+ matchesGoldenFile('dropdown_test.default.0.png'),
+ skip: !Platform.isLinux,
+ );
+ });
+
+ testWidgets('Expanded dropdown golden', (WidgetTester tester) async {
+ final Key buttonKey = new UniqueKey();
+ Widget build() => buildFrame(buttonKey: buttonKey, value: 'two', isExpanded: true);
+ await tester.pumpWidget(build());
+ final Finder buttonFinder = find.byKey(buttonKey);
+ assert(tester.renderObject(buttonFinder).attached);
+ await expectLater(
+ find.ancestor(of: buttonFinder, matching: find.byType(RepaintBoundary)).first,
+ matchesGoldenFile('dropdown_test.expanded.0.png'),
+ skip: !Platform.isLinux,
+ );
+ });
+
testWidgets('Dropdown button control test', (WidgetTester tester) async {
String value = 'one';
void didChangeValue(String newValue) {
@@ -337,6 +368,23 @@
});
}
+ testWidgets('Arrow icon aligns with the edge of button when expanded', (WidgetTester tester) async {
+ final Key buttonKey = new UniqueKey();
+
+ Widget build() => buildFrame(buttonKey: buttonKey, value: 'two', isExpanded: true);
+
+ await tester.pumpWidget(build());
+ final RenderBox buttonBox = tester.renderObject(find.byKey(buttonKey));
+ assert(buttonBox.attached);
+
+ final RenderBox arrowIcon = tester.renderObject(find.byIcon(Icons.arrow_drop_down));
+ assert(arrowIcon.attached);
+
+ // Arrow icon should be aligned with far right of button when expanded
+ expect(arrowIcon.localToGlobal(Offset.zero).dx,
+ buttonBox.size.centerRight(new Offset(-arrowIcon.size.width, 0.0)).dx);
+ });
+
testWidgets('Dropdown button with isDense:true aligns selected menu item', (WidgetTester tester) async {
final Key buttonKey = new UniqueKey();
const String value = 'two';