Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
e1c38aa0
Unverified
Commit
e1c38aa0
authored
Mar 07, 2018
by
Hans Muller
Committed by
GitHub
Mar 07, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Enable aligning a dropdown button's width with its menu's width (#14849)
parent
01d8e0a1
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
171 additions
and
16 deletions
+171
-16
button_theme.dart
packages/flutter/lib/src/material/button_theme.dart
+30
-7
dropdown.dart
packages/flutter/lib/src/material/dropdown.dart
+32
-8
theme_data.dart
packages/flutter/lib/src/material/theme_data.dart
+1
-1
button_theme_test.dart
packages/flutter/test/material/button_theme_test.dart
+108
-0
No files found.
packages/flutter/lib/src/material/button_theme.dart
View file @
e1c38aa0
...
...
@@ -64,16 +64,19 @@ class ButtonTheme extends InheritedWidget {
double
height:
36.0
,
EdgeInsetsGeometry
padding
,
ShapeBorder
shape
,
bool
alignedDropdown:
false
,
Widget
child
,
})
:
assert
(
textTheme
!=
null
),
assert
(
minWidth
!=
null
&&
minWidth
>=
0.0
),
assert
(
height
!=
null
&&
height
>=
0.0
),
assert
(
alignedDropdown
!=
null
),
data
=
new
ButtonThemeData
(
textTheme:
textTheme
,
minWidth:
minWidth
,
height:
height
,
padding:
padding
,
shape:
shape
,
alignedDropdown:
alignedDropdown
),
super
(
key:
key
,
child:
child
);
...
...
@@ -98,16 +101,19 @@ class ButtonTheme extends InheritedWidget {
double
height:
36.0
,
EdgeInsetsGeometry
padding:
const
EdgeInsets
.
symmetric
(
horizontal:
8.0
),
ShapeBorder
shape
,
bool
alignedDropdown:
false
,
Widget
child
,
})
:
assert
(
textTheme
!=
null
),
assert
(
minWidth
!=
null
&&
minWidth
>=
0.0
),
assert
(
height
!=
null
&&
height
>=
0.0
),
assert
(
alignedDropdown
!=
null
),
data
=
new
ButtonThemeData
(
textTheme:
textTheme
,
minWidth:
minWidth
,
height:
height
,
padding:
padding
,
shape:
shape
,
alignedDropdown:
alignedDropdown
,
),
super
(
key:
key
,
child:
child
);
...
...
@@ -146,9 +152,11 @@ class ButtonThemeData extends Diagnosticable {
this
.
height
:
36.0
,
EdgeInsetsGeometry
padding
,
ShapeBorder
shape
,
this
.
alignedDropdown
:
false
,
})
:
assert
(
textTheme
!=
null
),
assert
(
minWidth
!=
null
&&
minWidth
>=
0.0
),
assert
(
height
!=
null
&&
height
>=
0.0
),
assert
(
alignedDropdown
!=
null
),
_padding
=
padding
,
_shape
=
shape
;
...
...
@@ -229,6 +237,17 @@ class ButtonThemeData extends Diagnosticable {
}
final
ShapeBorder
_shape
;
/// If true, then a [DropdownButton] menu's width will match the button's
/// width.
///
/// If false (the default), then the dropdown's menu will be wider than
/// its button. In either case the dropdown button will line up the leading
/// edge of the menu's value with the leading edge of the values
/// displayed by the menu items.
///
/// This property only affects [DropdownButton] and its menu.
final
bool
alignedDropdown
;
@override
bool
operator
==(
dynamic
other
)
{
if
(
other
.
runtimeType
!=
runtimeType
)
...
...
@@ -238,7 +257,8 @@ class ButtonThemeData extends Diagnosticable {
&&
minWidth
==
typedOther
.
minWidth
&&
height
==
typedOther
.
height
&&
padding
==
typedOther
.
padding
&&
shape
==
typedOther
.
shape
;
&&
shape
==
typedOther
.
shape
&&
alignedDropdown
==
typedOther
.
alignedDropdown
;
}
@override
...
...
@@ -249,6 +269,7 @@ class ButtonThemeData extends Diagnosticable {
height
,
padding
,
shape
,
alignedDropdown
,
);
}
...
...
@@ -256,13 +277,15 @@ class ButtonThemeData extends Diagnosticable {
void
debugFillProperties
(
DiagnosticPropertiesBuilder
description
)
{
super
.
debugFillProperties
(
description
);
final
ButtonThemeData
defaultTheme
=
const
ButtonThemeData
();
description
.
add
(
new
EnumProperty
<
ButtonTextTheme
>(
'textTheme'
,
textTheme
,
defaultValue:
defaultTheme
.
textTheme
));
description
.
add
(
new
EnumProperty
<
ButtonTextTheme
>(
'textTheme'
,
textTheme
,
defaultValue:
defaultTheme
.
textTheme
));
description
.
add
(
new
DoubleProperty
(
'minWidth'
,
minWidth
,
defaultValue:
defaultTheme
.
minWidth
));
description
.
add
(
new
DoubleProperty
(
'height'
,
height
,
defaultValue:
defaultTheme
.
height
));
description
.
add
(
new
DiagnosticsProperty
<
EdgeInsetsGeometry
>(
'padding'
,
padding
,
defaultValue:
defaultTheme
.
padding
));
description
.
add
(
new
DiagnosticsProperty
<
ShapeBorder
>(
'shape'
,
shape
,
defaultValue:
defaultTheme
.
shape
));
description
.
add
(
new
DiagnosticsProperty
<
EdgeInsetsGeometry
>(
'padding'
,
padding
,
defaultValue:
defaultTheme
.
padding
));
description
.
add
(
new
DiagnosticsProperty
<
ShapeBorder
>(
'shape'
,
shape
,
defaultValue:
defaultTheme
.
shape
));
description
.
add
(
new
FlagProperty
(
'alignedDropdown'
,
value:
alignedDropdown
,
defaultValue:
defaultTheme
.
alignedDropdown
,
ifTrue:
'dropdown width matches button'
,
));
}
}
packages/flutter/lib/src/material/dropdown.dart
View file @
e1c38aa0
...
...
@@ -7,6 +7,7 @@ import 'dart:math' as math;
import
'package:flutter/foundation.dart'
;
import
'package:flutter/widgets.dart'
;
import
'button_theme.dart'
;
import
'colors.dart'
;
import
'constants.dart'
;
import
'debug.dart'
;
...
...
@@ -21,7 +22,11 @@ import 'theme.dart';
const
Duration
_kDropdownMenuDuration
=
const
Duration
(
milliseconds:
300
);
const
double
_kMenuItemHeight
=
48.0
;
const
double
_kDenseButtonHeight
=
24.0
;
const
EdgeInsets
_kMenuHorizontalPadding
=
const
EdgeInsets
.
symmetric
(
horizontal:
16.0
);
const
EdgeInsets
_kMenuItemPadding
=
const
EdgeInsets
.
symmetric
(
horizontal:
16.0
);
const
EdgeInsetsGeometry
_kAlignedButtonPadding
=
const
EdgeInsetsDirectional
.
only
(
start:
16.0
,
end:
4.0
);
const
EdgeInsets
_kUnalignedButtonPadding
=
EdgeInsets
.
zero
;
const
EdgeInsets
_kAlignedMenuMargin
=
EdgeInsets
.
zero
;
const
EdgeInsetsGeometry
_kUnalignedMenuMargin
=
const
EdgeInsetsDirectional
.
only
(
start:
16.0
,
end:
24.0
);
class
_DropdownMenuPainter
extends
CustomPainter
{
_DropdownMenuPainter
({
...
...
@@ -91,10 +96,12 @@ class _DropdownScrollBehavior extends ScrollBehavior {
class
_DropdownMenu
<
T
>
extends
StatefulWidget
{
const
_DropdownMenu
({
Key
key
,
this
.
padding
,
this
.
route
,
})
:
super
(
key:
key
);
final
_DropdownRoute
<
T
>
route
;
final
EdgeInsets
padding
;
@override
_DropdownMenuState
<
T
>
createState
()
=>
new
_DropdownMenuState
<
T
>();
...
...
@@ -149,7 +156,7 @@ class _DropdownMenuState<T> extends State<_DropdownMenu<T>> {
opacity:
opacity
,
child:
new
InkWell
(
child:
new
Container
(
padding:
_kMenuHorizontalP
adding
,
padding:
widget
.
p
adding
,
child:
route
.
items
[
itemIndex
],
),
onTap:
()
=>
Navigator
.
pop
(
...
...
@@ -212,7 +219,7 @@ class _DropdownMenuRouteLayout<T> extends SingleChildLayoutDelegate {
final
double
maxHeight
=
math
.
max
(
0.0
,
constraints
.
maxHeight
-
2
*
_kMenuItemHeight
);
// The width of a menu should be at most the view width. This ensures that
// the menu does not extend past the left and right edges of the screen.
final
double
width
=
math
.
min
(
constraints
.
maxWidth
,
buttonRect
.
width
+
8.0
);
final
double
width
=
math
.
min
(
constraints
.
maxWidth
,
buttonRect
.
width
);
return
new
BoxConstraints
(
minWidth:
width
,
maxWidth:
width
,
...
...
@@ -238,7 +245,7 @@ class _DropdownMenuRouteLayout<T> extends SingleChildLayoutDelegate {
double
left
;
switch
(
textDirection
)
{
case
TextDirection
.
rtl
:
left
=
buttonRect
.
right
.
clamp
(
0.0
,
size
.
width
-
childS
ize
.
width
)
-
childSize
.
width
;
left
=
buttonRect
.
right
.
clamp
(
0.0
,
s
ize
.
width
)
-
childSize
.
width
;
break
;
case
TextDirection
.
ltr
:
left
=
buttonRect
.
left
.
clamp
(
0.0
,
size
.
width
-
childSize
.
width
);
...
...
@@ -279,6 +286,7 @@ class _DropdownRouteResult<T> {
class
_DropdownRoute
<
T
>
extends
PopupRoute
<
_DropdownRouteResult
<
T
>>
{
_DropdownRoute
({
this
.
items
,
this
.
padding
,
this
.
buttonRect
,
this
.
selectedIndex
,
this
.
elevation
:
8
,
...
...
@@ -288,6 +296,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
})
:
assert
(
style
!=
null
);
final
List
<
DropdownMenuItem
<
T
>>
items
;
final
EdgeInsetsGeometry
padding
;
final
Rect
buttonRect
;
final
int
selectedIndex
;
final
int
elevation
;
...
...
@@ -336,7 +345,12 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
scrollController
=
new
ScrollController
(
initialScrollOffset:
scrollOffset
);
}
Widget
menu
=
new
_DropdownMenu
<
T
>(
route:
this
);
final
TextDirection
textDirection
=
Directionality
.
of
(
context
);
Widget
menu
=
new
_DropdownMenu
<
T
>(
route:
this
,
padding:
padding
.
resolve
(
textDirection
),
);
if
(
theme
!=
null
)
menu
=
new
Theme
(
data:
theme
,
child:
menu
);
...
...
@@ -353,7 +367,7 @@ class _DropdownRoute<T> extends PopupRoute<_DropdownRouteResult<T>> {
buttonRect:
buttonRect
,
menuTop:
menuTop
,
menuHeight:
menuHeight
,
textDirection:
Directionality
.
of
(
context
)
,
textDirection:
textDirection
,
),
child:
menu
,
);
...
...
@@ -566,11 +580,16 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
void
_handleTap
()
{
final
RenderBox
itemBox
=
context
.
findRenderObject
();
final
Rect
itemRect
=
itemBox
.
localToGlobal
(
Offset
.
zero
)
&
itemBox
.
size
;
final
TextDirection
textDirection
=
Directionality
.
of
(
context
);
final
EdgeInsetsGeometry
menuMargin
=
ButtonTheme
.
of
(
context
).
alignedDropdown
?
_kAlignedMenuMargin
:
_kUnalignedMenuMargin
;
assert
(
_dropdownRoute
==
null
);
_dropdownRoute
=
new
_DropdownRoute
<
T
>(
items:
widget
.
items
,
buttonRect:
_kMenuHorizontalPadding
.
inflateRect
(
itemRect
),
buttonRect:
menuMargin
.
resolve
(
textDirection
).
inflateRect
(
itemRect
),
padding:
_kMenuItemPadding
.
resolve
(
textDirection
),
selectedIndex:
_selectedIndex
??
0
,
elevation:
widget
.
elevation
,
theme:
Theme
.
of
(
context
,
shadowThemeOnly:
true
),
...
...
@@ -613,9 +632,14 @@ class _DropdownButtonState<T> extends State<DropdownButton<T>> with WidgetsBindi
));
}
final
EdgeInsetsGeometry
padding
=
ButtonTheme
.
of
(
context
).
alignedDropdown
?
_kAlignedButtonPadding
:
_kUnalignedButtonPadding
;
Widget
result
=
new
DefaultTextStyle
(
style:
_textStyle
,
child:
new
SizedBox
(
child:
new
Container
(
padding:
padding
.
resolve
(
Directionality
.
of
(
context
)),
height:
widget
.
isDense
?
_denseButtonHeight
:
null
,
child:
new
Row
(
mainAxisAlignment:
MainAxisAlignment
.
spaceBetween
,
...
...
packages/flutter/lib/src/material/theme_data.dart
View file @
e1c38aa0
...
...
@@ -491,7 +491,7 @@ class ThemeData extends Diagnosticable {
Color
unselectedWidgetColor
,
Color
disabledColor
,
Color
buttonColor
,
Color
buttonTheme
,
ButtonThemeData
buttonTheme
,
Color
secondaryHeaderColor
,
Color
textSelectionColor
,
Color
textSelectionHandleColor
,
...
...
packages/flutter/test/material/button_theme_test.dart
View file @
e1c38aa0
...
...
@@ -14,6 +14,7 @@ void main() {
expect
(
theme
.
shape
,
const
RoundedRectangleBorder
(
borderRadius:
const
BorderRadius
.
all
(
const
Radius
.
circular
(
2.0
)),
));
expect
(
theme
.
alignedDropdown
,
false
);
});
test
(
'ButtonThemeData default overrides'
,
()
{
...
...
@@ -23,11 +24,13 @@ void main() {
height:
200.0
,
padding:
EdgeInsets
.
zero
,
shape:
const
RoundedRectangleBorder
(),
alignedDropdown:
true
,
);
expect
(
theme
.
textTheme
,
ButtonTextTheme
.
primary
);
expect
(
theme
.
constraints
,
const
BoxConstraints
(
minWidth:
100.0
,
minHeight:
200.0
));
expect
(
theme
.
padding
,
EdgeInsets
.
zero
);
expect
(
theme
.
shape
,
const
RoundedRectangleBorder
());
expect
(
theme
.
alignedDropdown
,
true
);
});
testWidgets
(
'ButtonTheme defaults'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -173,4 +176,109 @@ void main() {
expect
(
tester
.
widget
<
Material
>(
find
.
byType
(
Material
)).
color
,
const
Color
(
0xFF00FF00
));
expect
(
tester
.
getSize
(
find
.
byType
(
Material
)),
const
Size
(
100.0
,
200.0
));
});
testWidgets
(
'ButtonTheme alignedDropdown'
,
(
WidgetTester
tester
)
async
{
final
Key
dropdownKey
=
new
UniqueKey
();
Widget
buildFrame
({
bool
alignedDropdown
,
TextDirection
textDirection
})
{
return
new
MaterialApp
(
builder:
(
BuildContext
context
,
Widget
child
)
{
return
new
Directionality
(
textDirection:
textDirection
,
child:
child
,
);
},
home:
new
ButtonTheme
(
alignedDropdown:
alignedDropdown
,
child:
new
Material
(
child:
new
Builder
(
builder:
(
BuildContext
context
)
{
return
new
Container
(
alignment:
Alignment
.
center
,
child:
new
DropdownButtonHideUnderline
(
child:
new
Container
(
width:
200.0
,
child:
new
DropdownButton
<
String
>(
key:
dropdownKey
,
onChanged:
(
String
value
)
{
},
value:
'foo'
,
items:
const
<
DropdownMenuItem
<
String
>>[
const
DropdownMenuItem
<
String
>(
value:
'foo'
,
child:
const
Text
(
'foo'
),
),
const
DropdownMenuItem
<
String
>(
value:
'bar'
,
child:
const
Text
(
'bar'
),
),
],
),
),
),
);
},
),
),
),
);
}
final
Finder
button
=
find
.
byKey
(
dropdownKey
);
final
Finder
menu
=
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_DropdownMenu<String>'
);
await
tester
.
pumpWidget
(
buildFrame
(
alignedDropdown:
false
,
textDirection:
TextDirection
.
ltr
,
),
);
await
tester
.
tap
(
button
);
await
tester
.
pumpAndSettle
();
// 240 = 200.0 (button width) + _kUnalignedMenuMargin (20.0 left and right)
expect
(
tester
.
getSize
(
button
).
width
,
200.0
);
expect
(
tester
.
getSize
(
menu
).
width
,
240.0
);
// Dismiss the menu.
await
tester
.
tapAt
(
Offset
.
zero
);
await
tester
.
pumpAndSettle
();
expect
(
menu
,
findsNothing
);
await
tester
.
pumpWidget
(
buildFrame
(
alignedDropdown:
true
,
textDirection:
TextDirection
.
ltr
,
),
);
await
tester
.
tap
(
button
);
await
tester
.
pumpAndSettle
();
// Aligneddropdown: true means the button and menu widths match
expect
(
tester
.
getSize
(
button
).
width
,
200.0
);
expect
(
tester
.
getSize
(
menu
).
width
,
200.0
);
// There are two 'foo' widgets: the selected menu item's label and the drop
// down button's label. The should both appear at the same location.
final
Finder
fooText
=
find
.
text
(
'foo'
);
expect
(
fooText
,
findsNWidgets
(
2
));
expect
(
tester
.
getRect
(
fooText
.
at
(
0
)),
tester
.
getRect
(
fooText
.
at
(
1
)));
// Dismiss the menu.
await
tester
.
tapAt
(
Offset
.
zero
);
await
tester
.
pumpAndSettle
();
expect
(
menu
,
findsNothing
);
// Same test as above execpt RTL
await
tester
.
pumpWidget
(
buildFrame
(
alignedDropdown:
true
,
textDirection:
TextDirection
.
rtl
,
),
);
await
tester
.
tap
(
button
);
await
tester
.
pumpAndSettle
();
expect
(
fooText
,
findsNWidgets
(
2
));
expect
(
tester
.
getRect
(
fooText
.
at
(
0
)),
tester
.
getRect
(
fooText
.
at
(
1
)));
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment