Unverified Commit 9a290c17 authored by Michael Klimushyn's avatar Michael Klimushyn Committed by GitHub

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
parent 05d557b9
555a2f96848c31b8c0303555535d6b5e20686e6e 5b0696a651738116b4d25a5b0a946b0951e45e6f
...@@ -484,6 +484,7 @@ class DropdownButton<T> extends StatefulWidget { ...@@ -484,6 +484,7 @@ class DropdownButton<T> extends StatefulWidget {
this.style, this.style,
this.iconSize = 24.0, this.iconSize = 24.0,
this.isDense = false, this.isDense = false,
this.isExpanded = false,
}) : assert(items != null), }) : assert(items != null),
assert(value == null || items.where((DropdownMenuItem<T> item) => item.value == value).length == 1), assert(value == null || items.where((DropdownMenuItem<T> item) => item.value == value).length == 1),
super(key: key); super(key: key);
...@@ -529,6 +530,13 @@ class DropdownButton<T> extends StatefulWidget { ...@@ -529,6 +530,13 @@ class DropdownButton<T> extends StatefulWidget {
/// its own decorations, like [InputDecorator]. /// its own decorations, like [InputDecorator].
final bool isDense; 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 @override
_DropdownButtonState<T> createState() => new _DropdownButtonState<T>(); _DropdownButtonState<T> createState() => new _DropdownButtonState<T>();
} }
...@@ -643,6 +651,14 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -643,6 +651,14 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
? _kAlignedButtonPadding ? _kAlignedButtonPadding
: _kUnalignedButtonPadding; : _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( Widget result = new DefaultTextStyle(
style: _textStyle, style: _textStyle,
child: new Container( child: new Container(
...@@ -652,13 +668,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi ...@@ -652,13 +668,7 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
// If value is null (then _selectedIndex is null) then we display widget.isExpanded ? new Expanded(child: innerItemsWidget) : innerItemsWidget,
// the hint or nothing at all.
new IndexedStack(
index: _selectedIndex ?? hintIndex,
alignment: AlignmentDirectional.centerStart,
children: items,
),
new Icon(Icons.arrow_drop_down, new Icon(Icons.arrow_drop_down,
size: widget.iconSize, size: widget.iconSize,
// These colors are not defined in the Material Design spec. // These colors are not defined in the Material Design spec.
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:io';
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:ui' show window; import 'dart:ui' show window;
...@@ -23,6 +24,7 @@ Widget buildFrame({ ...@@ -23,6 +24,7 @@ Widget buildFrame({
String value = 'two', String value = 'two',
ValueChanged<String> onChanged, ValueChanged<String> onChanged,
bool isDense = false, bool isDense = false,
bool isExpanded = false,
Widget hint, Widget hint,
List<String> items = menuItems, List<String> items = menuItems,
Alignment alignment = Alignment.center, Alignment alignment = Alignment.center,
...@@ -33,12 +35,14 @@ Widget buildFrame({ ...@@ -33,12 +35,14 @@ Widget buildFrame({
child: new Material( child: new Material(
child: new Align( child: new Align(
alignment: alignment, alignment: alignment,
child: new RepaintBoundary(
child: new DropdownButton<String>( child: new DropdownButton<String>(
key: buttonKey, key: buttonKey,
value: value, value: value,
hint: hint, hint: hint,
onChanged: onChanged, onChanged: onChanged,
isDense: isDense, isDense: isDense,
isExpanded: isExpanded,
items: items.map((String item) { items: items.map((String item) {
return new DropdownMenuItem<String>( return new DropdownMenuItem<String>(
key: new ValueKey<String>(item), key: new ValueKey<String>(item),
...@@ -46,6 +50,7 @@ Widget buildFrame({ ...@@ -46,6 +50,7 @@ Widget buildFrame({
child: new Text(item, key: new ValueKey<String>(item + 'Text')), child: new Text(item, key: new ValueKey<String>(item + 'Text')),
); );
}).toList(), }).toList(),
)
), ),
), ),
), ),
...@@ -108,6 +113,32 @@ bool sameGeometry(RenderBox box1, RenderBox box2) { ...@@ -108,6 +113,32 @@ bool sameGeometry(RenderBox box1, RenderBox box2) {
} }
void main() { 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 { testWidgets('Dropdown button control test', (WidgetTester tester) async {
String value = 'one'; String value = 'one';
void didChangeValue(String newValue) { void didChangeValue(String newValue) {
...@@ -337,6 +368,23 @@ void main() { ...@@ -337,6 +368,23 @@ void main() {
}); });
} }
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 { testWidgets('Dropdown button with isDense:true aligns selected menu item', (WidgetTester tester) async {
final Key buttonKey = new UniqueKey(); final Key buttonKey = new UniqueKey();
const String value = 'two'; const String value = 'two';
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment