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
79d0358e
Unverified
Commit
79d0358e
authored
Jan 15, 2021
by
xubaolin
Committed by
GitHub
Jan 15, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update PopupMenuButton to match Material Design spec (#70160)
parent
7ee910b0
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
284 additions
and
77 deletions
+284
-77
popup_menu.dart
packages/flutter/lib/src/material/popup_menu.dart
+115
-35
popup_menu_test.dart
packages/flutter/test/material/popup_menu_test.dart
+151
-26
text_test.dart
packages/flutter_localizations/test/text_test.dart
+18
-16
No files found.
packages/flutter/lib/src/material/popup_menu.dart
View file @
79d0358e
...
...
@@ -599,7 +599,15 @@ class _PopupMenu<T> extends StatelessWidget {
// Positioning of the menu on the screen.
class
_PopupMenuRouteLayout
extends
SingleChildLayoutDelegate
{
_PopupMenuRouteLayout
(
this
.
position
,
this
.
itemSizes
,
this
.
selectedItemIndex
,
this
.
textDirection
);
_PopupMenuRouteLayout
(
this
.
position
,
this
.
itemSizes
,
this
.
selectedItemIndex
,
this
.
textDirection
,
this
.
topPadding
,
this
.
bottomPadding
,
this
.
placement
,
);
// Rectangle of underlying button, relative to the overlay's dimensions.
final
RelativeRect
position
;
...
...
@@ -615,6 +623,15 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
// Whether to prefer going to the left or to the right.
final
TextDirection
textDirection
;
// Top padding of unsafe area.
final
double
topPadding
;
// Bottom padding of unsafe area.
final
double
bottomPadding
;
// The placement of the menu.
final
PopupMenuPlacement
placement
;
// We put the child wherever position specifies, so long as it will fit within
// the specified parent size padded (inset) by 8. If necessary, we adjust the
// child's position so that it fits.
...
...
@@ -624,7 +641,8 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
// The menu can be at most the size of the overlay minus 8.0 pixels in each
// direction.
return
BoxConstraints
.
loose
(
constraints
.
biggest
-
const
Offset
(
_kMenuScreenPadding
*
2.0
,
_kMenuScreenPadding
*
2.0
)
as
Size
,
constraints
.
biggest
-
Offset
(
_kMenuScreenPadding
*
2.0
,
_kMenuScreenPadding
*
2.0
+
topPadding
+
bottomPadding
)
as
Size
,
);
}
...
...
@@ -634,14 +652,23 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
// childSize: The size of the menu, when fully open, as determined by
// getConstraintsForChild.
final
double
buttonHeight
=
size
.
height
-
position
.
top
-
position
.
bottom
;
// Find the ideal vertical position.
double
y
=
position
.
top
;
// Default vertical position is below the element that generates it.
double
y
=
placement
==
PopupMenuPlacement
.
belowButton
?
position
.
top
+
buttonHeight
:
position
.
top
;
if
(
selectedItemIndex
!=
null
&&
itemSizes
!=
null
)
{
double
selectedItemOffset
=
_kMenuVerticalPadding
;
for
(
int
index
=
0
;
index
<
selectedItemIndex
!;
index
+=
1
)
selectedItemOffset
+=
itemSizes
[
index
]!.
height
;
selectedItemOffset
+=
itemSizes
[
selectedItemIndex
!]!.
height
/
2
;
y
=
position
.
top
+
(
size
.
height
-
position
.
top
-
position
.
bottom
)
/
2.0
-
selectedItemOffset
;
if
(
placement
==
PopupMenuPlacement
.
belowButton
)
{
y
=
y
-
buttonHeight
/
2.0
-
selectedItemOffset
;
}
else
{
y
=
y
+
buttonHeight
/
2.0
-
selectedItemOffset
;
}
}
// Find the ideal horizontal position.
...
...
@@ -671,10 +698,10 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
x
=
_kMenuScreenPadding
;
else
if
(
x
+
childSize
.
width
>
size
.
width
-
_kMenuScreenPadding
)
x
=
size
.
width
-
childSize
.
width
-
_kMenuScreenPadding
;
if
(
y
<
_kMenuScreenPadding
)
y
=
_kMenuScreenPadding
;
if
(
y
<
_kMenuScreenPadding
+
topPadding
)
y
=
_kMenuScreenPadding
+
topPadding
;
else
if
(
y
+
childSize
.
height
>
size
.
height
-
_kMenuScreenPadding
)
y
=
size
.
height
-
childSize
.
height
-
_kMenuScreenPadding
;
y
=
size
.
height
-
bottomPadding
-
_kMenuScreenPadding
-
childSize
.
height
;
return
Offset
(
x
,
y
);
}
...
...
@@ -686,9 +713,9 @@ class _PopupMenuRouteLayout extends SingleChildLayoutDelegate {
assert
(
itemSizes
.
length
==
oldDelegate
.
itemSizes
.
length
);
return
position
!=
oldDelegate
.
position
||
selectedItemIndex
!=
oldDelegate
.
selectedItemIndex
||
textDirection
!=
oldDelegate
.
textDirection
||
!
listEquals
(
itemSizes
,
oldDelegate
.
itemSizes
);
||
selectedItemIndex
!=
oldDelegate
.
selectedItemIndex
||
textDirection
!=
oldDelegate
.
textDirection
||
!
listEquals
(
itemSizes
,
oldDelegate
.
itemSizes
);
}
}
...
...
@@ -703,6 +730,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
this
.
shape
,
this
.
color
,
required
this
.
capturedThemes
,
required
this
.
placement
,
})
:
itemSizes
=
List
<
Size
?>.
filled
(
items
.
length
,
null
);
final
RelativeRect
position
;
...
...
@@ -714,6 +742,7 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
final
ShapeBorder
?
shape
;
final
Color
?
color
;
final
CapturedThemes
capturedThemes
;
final
PopupMenuPlacement
placement
;
@override
Animation
<
double
>
createAnimation
()
{
...
...
@@ -749,20 +778,22 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
final
Widget
menu
=
_PopupMenu
<
T
>(
route:
this
,
semanticLabel:
semanticLabel
);
return
SafeArea
(
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
CustomSingleChildLayout
(
delegate:
_PopupMenuRouteLayout
(
position
,
itemSizes
,
selectedItemIndex
,
Directionality
.
of
(
context
),
),
child:
capturedThemes
.
wrap
(
menu
),
);
},
),
return
Builder
(
builder:
(
BuildContext
context
)
{
final
MediaQueryData
mediaQuery
=
MediaQuery
.
of
(
context
);
return
CustomSingleChildLayout
(
delegate:
_PopupMenuRouteLayout
(
position
,
itemSizes
,
selectedItemIndex
,
Directionality
.
of
(
context
),
mediaQuery
.
padding
.
top
,
mediaQuery
.
padding
.
bottom
,
placement
,
),
child:
capturedThemes
.
wrap
(
menu
),
);
},
);
}
}
...
...
@@ -832,6 +863,7 @@ Future<T?> showMenu<T>({
ShapeBorder
?
shape
,
Color
?
color
,
bool
useRootNavigator
=
false
,
PopupMenuPlacement
placement
=
PopupMenuPlacement
.
aboveButton
,
})
{
assert
(
context
!=
null
);
assert
(
position
!=
null
);
...
...
@@ -861,6 +893,7 @@ Future<T?> showMenu<T>({
shape:
shape
,
color:
color
,
capturedThemes:
InheritedTheme
.
capture
(
from:
context
,
to:
navigator
.
context
),
placement:
placement
,
));
}
...
...
@@ -883,6 +916,17 @@ typedef PopupMenuCanceled = void Function();
/// Used by [PopupMenuButton.itemBuilder].
typedef
PopupMenuItemBuilder
<
T
>
=
List
<
PopupMenuEntry
<
T
>>
Function
(
BuildContext
context
);
/// The placement of the menu popped up by press the [PopupMenuButton].
///
/// Used by [PopupMenuButton.placement].
enum
PopupMenuPlacement
{
/// The popup menu is positioned above the button that generates it.
aboveButton
,
/// The popup menu is positioned below the button that generates it.
belowButton
,
}
/// Displays a menu when pressed and calls [onSelected] when the menu is dismissed
/// because an item was selected. The value passed to [onSelected] is the value of
/// the selected menu item.
...
...
@@ -955,6 +999,7 @@ class PopupMenuButton<T> extends StatefulWidget {
this
.
shape
,
this
.
color
,
this
.
enableFeedback
,
this
.
placement
=
PopupMenuPlacement
.
aboveButton
,
})
:
assert
(
itemBuilder
!=
null
),
assert
(
offset
!=
null
),
assert
(
enabled
!=
null
),
...
...
@@ -1006,7 +1051,7 @@ class PopupMenuButton<T> extends StatefulWidget {
/// The offset applied to the Popup Menu Button.
///
/// When not set, the Popup Menu Button will be positioned directly
next to
/// When not set, the Popup Menu Button will be positioned directly
below
/// the button that was used to create it.
final
Offset
offset
;
...
...
@@ -1049,6 +1094,11 @@ class PopupMenuButton<T> extends StatefulWidget {
/// * [Feedback] for providing platform-specific feedback to certain actions.
final
bool
?
enableFeedback
;
/// The placement of the menu popped up by press the [PopupMenuButton].
///
/// Default to [PopupMenuPlacement.aboveButton].
final
PopupMenuPlacement
placement
;
/// If provided, the size of the [Icon].
///
/// If this property is null, the default size is 24.0 pixels.
...
...
@@ -1063,8 +1113,9 @@ class PopupMenuButton<T> extends StatefulWidget {
/// See [showButtonMenu] for a way to programmatically open the popup menu
/// of your button state.
class
PopupMenuButtonState
<
T
>
extends
State
<
PopupMenuButton
<
T
>>
{
final
GlobalKey
_menuButtonKey
=
GlobalKey
();
/// A method to show a popup menu with the items supplied to
/// [PopupMenuButton.itemBuilder] at the position
of
your [PopupMenuButton].
/// [PopupMenuButton.itemBuilder] at the position
below
your [PopupMenuButton].
///
/// By default, it is called when the user taps the button and [PopupMenuButton.enabled]
/// is set to `true`. Moreover, you can open the button by calling the method manually.
...
...
@@ -1073,8 +1124,14 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
/// show the menu of the button with `globalKey.currentState.showButtonMenu`.
void
showButtonMenu
()
{
final
PopupMenuThemeData
popupMenuTheme
=
PopupMenuTheme
.
of
(
context
);
final
RenderBox
button
=
context
.
findRenderObject
()!
as
RenderBox
;
final
RenderBox
overlay
=
Navigator
.
of
(
context
).
overlay
!.
context
.
findRenderObject
()!
as
RenderBox
;
final
RenderBox
button
;
if
(
widget
.
placement
==
PopupMenuPlacement
.
belowButton
)
{
button
=
_menuButtonKey
.
currentContext
!.
findRenderObject
()!
as
RenderBox
;
}
else
{
// Backward compatible.
button
=
context
.
findRenderObject
()!
as
RenderBox
;
}
final
RelativeRect
position
=
RelativeRect
.
fromRect
(
Rect
.
fromPoints
(
button
.
localToGlobal
(
widget
.
offset
,
ancestor:
overlay
),
...
...
@@ -1093,6 +1150,7 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
position:
position
,
shape:
widget
.
shape
??
popupMenuTheme
.
shape
,
color:
widget
.
color
??
popupMenuTheme
.
color
,
placement:
widget
.
placement
,
)
.
then
<
void
>((
T
?
newValue
)
{
if
(!
mounted
)
...
...
@@ -1132,18 +1190,40 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
child:
InkWell
(
onTap:
widget
.
enabled
?
showButtonMenu
:
null
,
canRequestFocus:
_canRequestFocus
,
child:
widget
.
child
,
enableFeedback:
enableFeedback
,
child:
LayoutBuilder
(
builder:
(
BuildContext
context
,
BoxConstraints
constraints
)
{
return
UnconstrainedBox
(
constrainedAxis:
Axis
.
horizontal
,
child:
LimitedBox
(
key:
_menuButtonKey
,
maxHeight:
constraints
.
maxHeight
,
child:
widget
.
child
,
),
);
},
),
),
);
return
IconButton
(
icon:
widget
.
icon
??
Icon
(
Icons
.
adaptive
.
more
),
padding:
widget
.
padding
,
iconSize:
widget
.
iconSize
??
24.0
,
tooltip:
widget
.
tooltip
??
MaterialLocalizations
.
of
(
context
).
showMenuTooltip
,
onPressed:
widget
.
enabled
?
showButtonMenu
:
null
,
enableFeedback:
enableFeedback
,
return
LayoutBuilder
(
builder:
(
BuildContext
context
,
BoxConstraints
constraints
)
{
return
UnconstrainedBox
(
constrainedAxis:
Axis
.
horizontal
,
child:
LimitedBox
(
maxHeight:
constraints
.
maxHeight
,
child:
IconButton
(
key:
_menuButtonKey
,
icon:
widget
.
icon
??
Icon
(
Icons
.
adaptive
.
more
),
padding:
widget
.
padding
,
iconSize:
widget
.
iconSize
??
24.0
,
tooltip:
widget
.
tooltip
??
MaterialLocalizations
.
of
(
context
).
showMenuTooltip
,
onPressed:
widget
.
enabled
?
showButtonMenu
:
null
,
enableFeedback:
enableFeedback
,
),
),
);
},
);
}
}
packages/flutter/test/material/popup_menu_test.dart
View file @
79d0358e
...
...
@@ -453,6 +453,7 @@ void main() {
testWidgets
(
'PopupMenu positioning'
,
(
WidgetTester
tester
)
async
{
final
Widget
testButton
=
PopupMenuButton
<
int
>(
placement:
PopupMenuPlacement
.
belowButton
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuItem
<
int
>>[
const
PopupMenuItem
<
int
>(
value:
1
,
child:
Text
(
'AAA'
)),
...
...
@@ -618,24 +619,24 @@ void main() {
});
}
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
topRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
8
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
topRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
8
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
topLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
8
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
topLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
8
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
topCenter
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
350.0
,
8
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
topCenter
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
450.0
,
8
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
centerRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
2
50.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
centerRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
2
50.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
centerLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
2
50.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
centerLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
2
50.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
center
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
350.0
,
2
50.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
center
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
450.0
,
2
50.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
ltr
,
Alignment
.
bottomRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
5
00
.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
rtl
,
Alignment
.
bottomRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
5
00
.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
ltr
,
Alignment
.
bottomLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
5
00
.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
rtl
,
Alignment
.
bottomLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
5
00
.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
ltr
,
Alignment
.
bottomCenter
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
350.0
,
5
00
.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
rtl
,
Alignment
.
bottomCenter
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
450.0
,
5
00
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
topRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
100
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
topRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
100
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
topLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
100
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
topLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
100
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
topCenter
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
350.0
,
100
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
topCenter
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
450.0
,
100
.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
centerRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
3
50.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
centerRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
3
50.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
centerLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
3
50.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
centerLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
3
50.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
ltr
,
Alignment
.
center
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
350.0
,
3
50.0
,
0.0
,
0.0
));
await
testPositioningDown
(
tester
,
TextDirection
.
rtl
,
Alignment
.
center
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
450.0
,
3
50.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
ltr
,
Alignment
.
bottomRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
5
92
.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
rtl
,
Alignment
.
bottomRight
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
792.0
,
5
92
.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
ltr
,
Alignment
.
bottomLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
5
92
.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
rtl
,
Alignment
.
bottomLeft
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
8.0
,
5
92
.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
ltr
,
Alignment
.
bottomCenter
,
TextDirection
.
ltr
,
const
Rect
.
fromLTWH
(
350.0
,
5
92
.0
,
0.0
,
0.0
));
await
testPositioningDownThenUp
(
tester
,
TextDirection
.
rtl
,
Alignment
.
bottomCenter
,
TextDirection
.
rtl
,
const
Rect
.
fromLTWH
(
450.0
,
5
92
.0
,
0.0
,
0.0
));
});
testWidgets
(
'PopupMenu positioning inside nested Overlay'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -653,6 +654,7 @@ void main() {
builder:
(
_
)
=>
Center
(
child:
PopupMenuButton
<
int
>(
key:
buttonKey
,
placement:
PopupMenuPlacement
.
belowButton
,
itemBuilder:
(
_
)
=>
<
PopupMenuItem
<
int
>>[
const
PopupMenuItem
<
int
>(
value:
1
,
child:
Text
(
'Item 1'
)),
const
PopupMenuItem
<
int
>(
value:
2
,
child:
Text
(
'Item 2'
)),
...
...
@@ -673,8 +675,8 @@ void main() {
await
tester
.
tap
(
buttonFinder
);
await
tester
.
pumpAndSettle
();
final
Offset
button
TopLeft
=
tester
.
getTop
Left
(
buttonFinder
);
expect
(
tester
.
getTopLeft
(
popupFinder
),
button
Top
Left
);
final
Offset
button
BottomLeft
=
tester
.
getBottom
Left
(
buttonFinder
);
expect
(
tester
.
getTopLeft
(
popupFinder
),
button
Bottom
Left
);
});
testWidgets
(
'PopupMenu positioning inside nested Navigator'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -695,6 +697,7 @@ void main() {
child:
Center
(
child:
PopupMenuButton
<
int
>(
key:
buttonKey
,
placement:
PopupMenuPlacement
.
belowButton
,
itemBuilder:
(
_
)
=>
<
PopupMenuItem
<
int
>>[
const
PopupMenuItem
<
int
>(
value:
1
,
child:
Text
(
'Item 1'
)),
const
PopupMenuItem
<
int
>(
value:
2
,
child:
Text
(
'Item 2'
)),
...
...
@@ -717,8 +720,8 @@ void main() {
await
tester
.
tap
(
buttonFinder
);
await
tester
.
pumpAndSettle
();
final
Offset
button
TopLeft
=
tester
.
getTop
Left
(
buttonFinder
);
expect
(
tester
.
getTopLeft
(
popupFinder
),
button
Top
Left
);
final
Offset
button
BottomLeft
=
tester
.
getBottom
Left
(
buttonFinder
);
expect
(
tester
.
getTopLeft
(
popupFinder
),
button
Bottom
Left
);
});
testWidgets
(
'PopupMenu removes MediaQuery padding'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -731,6 +734,7 @@ void main() {
),
child:
Material
(
child:
PopupMenuButton
<
int
>(
placement:
PopupMenuPlacement
.
belowButton
,
itemBuilder:
(
BuildContext
context
)
{
popupContext
=
context
;
return
<
PopupMenuItem
<
int
>>[
...
...
@@ -765,6 +769,7 @@ void main() {
testWidgets
(
'Popup Menu Offset Test'
,
(
WidgetTester
tester
)
async
{
PopupMenuButton
<
int
>
buildMenuButton
({
Offset
offset
=
const
Offset
(
0.0
,
0.0
)})
{
return
PopupMenuButton
<
int
>(
placement:
PopupMenuPlacement
.
belowButton
,
offset:
offset
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuItem
<
int
>>[
...
...
@@ -796,8 +801,10 @@ void main() {
await
tester
.
tap
(
find
.
byType
(
IconButton
));
await
tester
.
pumpAndSettle
();
// Initial state, the menu start at Offset(8.0, 8.0), the 8 pixels is edge padding when offset.dx < 8.0.
expect
(
tester
.
getTopLeft
(
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_PopupMenu<int?>'
)),
const
Offset
(
8.0
,
8.0
));
final
Size
iconSize
=
tester
.
getSize
(
find
.
byType
(
IconButton
));
// Initial state, the menu start at bottomLeft of the icon, the 8 pixels is edge padding when offset.dx < 8.0.
expect
(
tester
.
getTopLeft
(
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_PopupMenu<int?>'
)),
Offset
(
8.0
,
iconSize
.
height
));
// Collapse the menu.
await
tester
.
tap
(
find
.
byType
(
IconButton
));
...
...
@@ -817,8 +824,8 @@ void main() {
await
tester
.
tap
(
find
.
byType
(
IconButton
));
await
tester
.
pumpAndSettle
();
// This time the menu should start at Offset(50.0, 50.0), the padding only added when offset.dx < 8.0.
expect
(
tester
.
getTopLeft
(
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_PopupMenu<int?>'
)),
const
Offset
(
50.0
,
50.0
));
// This time the menu should start at Offset(50.0, 50.0
+ iconSize.height
), the padding only added when offset.dx < 8.0.
expect
(
tester
.
getTopLeft
(
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_PopupMenu<int?>'
)),
Offset
(
50.0
,
50.0
+
iconSize
.
height
));
});
testWidgets
(
'open PopupMenu has correct semantics'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -1804,6 +1811,124 @@ void main() {
);
});
testWidgets
(
'PopupMenu button can render at its "natural" size in AppBar'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
buttonKey
=
GlobalKey
();
Widget
buildFrame
(
double
width
,
double
height
)
{
return
MaterialApp
(
builder:
(
BuildContext
context
,
Widget
?
child
)
{
return
MediaQuery
(
data:
const
MediaQueryData
(
padding:
EdgeInsets
.
only
(
top:
32.0
,
bottom:
32.0
,
),
),
child:
child
!,
);
},
home:
Scaffold
(
appBar:
AppBar
(
toolbarHeight:
100.0
,
title:
const
Text
(
'PopupMenu Test'
),
actions:
<
Widget
>[
PopupMenuButton
<
int
>(
child:
SizedBox
(
key:
buttonKey
,
height:
height
,
width:
width
,
child:
const
ColoredBox
(
color:
Colors
.
pink
,
),
),
itemBuilder:
(
BuildContext
context
)
=>
<
PopupMenuEntry
<
int
>>[
const
PopupMenuItem
<
int
>(
child:
Text
(
'-1-'
),
value:
1
),
const
PopupMenuItem
<
int
>(
child:
Text
(
'-2-'
),
value:
2
),
const
PopupMenuItem
<
int
>(
child:
Text
(
'-3-'
),
value:
3
),
const
PopupMenuItem
<
int
>(
child:
Text
(
'-4-'
),
value:
4
),
const
PopupMenuItem
<
int
>(
child:
Text
(
'-5-'
),
value:
5
),
],
)],
),
body:
Container
(),
),
);
}
await
tester
.
pumpWidget
(
buildFrame
(
10.0
,
10.0
));
Size
buttonSize
=
tester
.
getSize
(
find
.
byKey
(
buttonKey
));
expect
(
buttonSize
,
const
Size
(
10.0
,
10.0
));
await
tester
.
pumpWidget
(
buildFrame
(
20.5
,
30.5
));
buttonSize
=
tester
.
getSize
(
find
.
byKey
(
buttonKey
));
expect
(
buttonSize
,
const
Size
(
20.5
,
30.5
));
await
tester
.
pumpWidget
(
buildFrame
(
20.0
,
100.0
));
buttonSize
=
tester
.
getSize
(
find
.
byKey
(
buttonKey
));
expect
(
buttonSize
,
const
Size
(
20.0
,
100.0
));
await
tester
.
pumpWidget
(
buildFrame
(
20.0
,
100.1
));
buttonSize
=
tester
.
getSize
(
find
.
byKey
(
buttonKey
));
expect
(
buttonSize
,
const
Size
(
20.0
,
100.0
));
// Do not overflow the AppBar.
});
testWidgets
(
'PopupMenu position test when have unsafe area'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
buttonKey
=
GlobalKey
();
final
GlobalKey
firstItemKey
=
GlobalKey
();
Widget
buildFrame
(
double
width
,
double
height
)
{
return
MaterialApp
(
builder:
(
BuildContext
context
,
Widget
?
child
)
{
return
MediaQuery
(
data:
const
MediaQueryData
(
padding:
EdgeInsets
.
only
(
top:
32.0
,
bottom:
32.0
,
),
),
child:
child
!,
);
},
home:
Scaffold
(
appBar:
AppBar
(
title:
const
Text
(
'PopupMenu Test'
),
actions:
<
Widget
>[
PopupMenuButton
<
int
>(
placement:
PopupMenuPlacement
.
belowButton
,
child:
SizedBox
(
key:
buttonKey
,
height:
height
,
width:
width
,
child:
const
ColoredBox
(
color:
Colors
.
pink
,
),
),
itemBuilder:
(
BuildContext
context
)
=>
<
PopupMenuEntry
<
int
>>[
PopupMenuItem
<
int
>(
key:
firstItemKey
,
child:
const
Text
(
'-1-'
),
value:
1
,
),
const
PopupMenuItem
<
int
>(
child:
Text
(
'-2-'
),
value:
2
,),
],
)],
),
body:
Container
(),
),
);
}
await
tester
.
pumpWidget
(
buildFrame
(
20.0
,
20.0
));
await
tester
.
tap
(
find
.
byKey
(
buttonKey
));
await
tester
.
pumpAndSettle
();
final
Offset
button
=
tester
.
getBottomRight
(
find
.
byKey
(
buttonKey
));
final
Offset
popupMenu
=
tester
.
getTopRight
(
find
.
byKey
(
firstItemKey
));
// The menu should popup below the button.
// The 8.0 pixels are [_kMenuScreenPadding] and [_kMenuVerticalPadding].
expect
(
popupMenu
,
Offset
(
button
.
dx
-
8.0
,
button
.
dy
+
8.0
));
});
group
(
'feedback'
,
()
{
late
FeedbackTester
feedback
;
...
...
packages/flutter_localizations/test/text_test.dart
View file @
79d0358e
...
...
@@ -38,6 +38,7 @@ void main() {
key:
targetKey
,
builder:
(
BuildContext
context
)
{
return
PopupMenuButton
<
int
>(
placement:
PopupMenuPlacement
.
belowButton
,
onSelected:
(
int
value
)
{
Navigator
.
pushNamed
(
context
,
'/next'
);
},
...
...
@@ -78,20 +79,20 @@ void main() {
Offset
bottomLeft
=
tester
.
getBottomLeft
(
find
.
text
(
'hello, world'
));
Offset
bottomRight
=
tester
.
getBottomRight
(
find
.
text
(
'hello, world'
));
expect
(
topLeft
,
const
Offset
(
392.0
,
299
.5
));
expect
(
topRight
,
const
Offset
(
596.0
,
299
.5
));
expect
(
bottomLeft
,
const
Offset
(
392.0
,
3
16
.5
));
expect
(
bottomRight
,
const
Offset
(
596.0
,
3
16
.5
));
expect
(
topLeft
,
const
Offset
(
392.0
,
347
.5
));
expect
(
topRight
,
const
Offset
(
596.0
,
347
.5
));
expect
(
bottomLeft
,
const
Offset
(
392.0
,
3
64
.5
));
expect
(
bottomRight
,
const
Offset
(
596.0
,
3
64
.5
));
topLeft
=
tester
.
getTopLeft
(
find
.
text
(
'你好,世界'
));
topRight
=
tester
.
getTopRight
(
find
.
text
(
'你好,世界'
));
bottomLeft
=
tester
.
getBottomLeft
(
find
.
text
(
'你好,世界'
));
bottomRight
=
tester
.
getBottomRight
(
find
.
text
(
'你好,世界'
));
expect
(
topLeft
,
const
Offset
(
392.0
,
3
47
.5
));
expect
(
topRight
,
const
Offset
(
477.0
,
3
47
.5
));
expect
(
bottomLeft
,
const
Offset
(
392.0
,
364
.5
));
expect
(
bottomRight
,
const
Offset
(
477.0
,
364
.5
));
expect
(
topLeft
,
const
Offset
(
392.0
,
3
95
.5
));
expect
(
topRight
,
const
Offset
(
477.0
,
3
95
.5
));
expect
(
bottomLeft
,
const
Offset
(
392.0
,
412
.5
));
expect
(
bottomRight
,
const
Offset
(
477.0
,
412
.5
));
});
testWidgets
(
'Text baseline with EN locale'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -124,6 +125,7 @@ void main() {
key:
targetKey
,
builder:
(
BuildContext
context
)
{
return
PopupMenuButton
<
int
>(
placement:
PopupMenuPlacement
.
belowButton
,
onSelected:
(
int
value
)
{
Navigator
.
pushNamed
(
context
,
'/next'
);
},
...
...
@@ -164,19 +166,19 @@ void main() {
Offset
bottomLeft
=
tester
.
getBottomLeft
(
find
.
text
(
'hello, world'
));
Offset
bottomRight
=
tester
.
getBottomRight
(
find
.
text
(
'hello, world'
));
expect
(
topLeft
,
const
Offset
(
392.0
,
3
00
.0
));
expect
(
topRight
,
const
Offset
(
584.0
,
3
00
.0
));
expect
(
bottomLeft
,
const
Offset
(
392.0
,
3
16
));
expect
(
bottomRight
,
const
Offset
(
584.0
,
3
16
));
expect
(
topLeft
,
const
Offset
(
392.0
,
3
48
.0
));
expect
(
topRight
,
const
Offset
(
584.0
,
3
48
.0
));
expect
(
bottomLeft
,
const
Offset
(
392.0
,
3
64
));
expect
(
bottomRight
,
const
Offset
(
584.0
,
3
64
));
topLeft
=
tester
.
getTopLeft
(
find
.
text
(
'你好,世界'
));
topRight
=
tester
.
getTopRight
(
find
.
text
(
'你好,世界'
));
bottomLeft
=
tester
.
getBottomLeft
(
find
.
text
(
'你好,世界'
));
bottomRight
=
tester
.
getBottomRight
(
find
.
text
(
'你好,世界'
));
expect
(
topLeft
,
const
Offset
(
392.0
,
3
48
.0
));
expect
(
topRight
,
const
Offset
(
472.0
,
3
48
.0
));
expect
(
bottomLeft
,
const
Offset
(
392.0
,
364
.0
));
expect
(
bottomRight
,
const
Offset
(
472.0
,
364
.0
));
expect
(
topLeft
,
const
Offset
(
392.0
,
3
96
.0
));
expect
(
topRight
,
const
Offset
(
472.0
,
3
96
.0
));
expect
(
bottomLeft
,
const
Offset
(
392.0
,
412
.0
));
expect
(
bottomRight
,
const
Offset
(
472.0
,
412
.0
));
});
}
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