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
f1d88586
Unverified
Commit
f1d88586
authored
Mar 05, 2022
by
Taha Tesser
Committed by
GitHub
Mar 05, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Make popup menu position configurable (#98979)
parent
03c92a99
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
161 additions
and
6 deletions
+161
-6
popup_menu.dart
packages/flutter/lib/src/material/popup_menu.dart
+32
-6
popup_menu_test.dart
packages/flutter/test/material/popup_menu_test.dart
+129
-0
No files found.
packages/flutter/lib/src/material/popup_menu.dart
View file @
f1d88586
...
...
@@ -38,6 +38,14 @@ const double _kMenuVerticalPadding = 8.0;
const
double
_kMenuWidthStep
=
56.0
;
const
double
_kMenuScreenPadding
=
8.0
;
/// Used to configure how the [PopupMenuButton] positions its popup menu.
enum
PopupMenuPosition
{
/// Menu is positioned over the anchor.
over
,
/// Menu is positioned under the anchor.
under
,
}
/// A base class for entries in a material design popup menu.
///
/// The popup menu widget uses this interface to interact with the menu items.
...
...
@@ -977,8 +985,8 @@ class PopupMenuButton<T> extends StatefulWidget {
this
.
color
,
this
.
enableFeedback
,
this
.
constraints
,
this
.
position
=
PopupMenuPosition
.
over
,
})
:
assert
(
itemBuilder
!=
null
),
assert
(
offset
!=
null
),
assert
(
enabled
!=
null
),
assert
(
!(
child
!=
null
&&
icon
!=
null
),
...
...
@@ -1033,10 +1041,10 @@ class PopupMenuButton<T> extends StatefulWidget {
/// and the button will behave like an [IconButton].
final
Widget
?
icon
;
/// The offset applied to the Popup Menu Button.
/// The offset is applied relative to the initial position
/// set by the [position].
///
/// When not set, the Popup Menu Button will be positioned directly next to
/// the button that was used to create it.
/// When not set, the offset defaults to [Offset.zero].
final
Offset
offset
;
/// Whether this popup menu button is interactive.
...
...
@@ -1099,6 +1107,15 @@ class PopupMenuButton<T> extends StatefulWidget {
/// the default maximum width.
final
BoxConstraints
?
constraints
;
/// Whether the popup menu is positioned over or under the popup menu button.
///
/// [offset] is used to change the position of the popup menu relative to the
/// position set by this parameter.
///
/// When not set, the position defaults to [PopupMenuPosition.over] which makes the
/// popup menu appear directly over the button that was used to create it.
final
PopupMenuPosition
position
;
@override
PopupMenuButtonState
<
T
>
createState
()
=>
PopupMenuButtonState
<
T
>();
}
...
...
@@ -1120,10 +1137,19 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
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
Offset
offset
;
switch
(
widget
.
position
)
{
case
PopupMenuPosition
.
over
:
offset
=
widget
.
offset
;
break
;
case
PopupMenuPosition
.
under
:
offset
=
Offset
(
0.0
,
button
.
size
.
height
-
(
widget
.
padding
.
vertical
/
2
))
+
widget
.
offset
;
break
;
}
final
RelativeRect
position
=
RelativeRect
.
fromRect
(
Rect
.
fromPoints
(
button
.
localToGlobal
(
widget
.
offset
,
ancestor:
overlay
),
button
.
localToGlobal
(
button
.
size
.
bottomRight
(
Offset
.
zero
)
+
widget
.
offset
,
ancestor:
overlay
),
button
.
localToGlobal
(
offset
,
ancestor:
overlay
),
button
.
localToGlobal
(
button
.
size
.
bottomRight
(
Offset
.
zero
)
+
offset
,
ancestor:
overlay
),
),
Offset
.
zero
&
overlay
.
size
,
);
...
...
packages/flutter/test/material/popup_menu_test.dart
View file @
f1d88586
...
...
@@ -2569,6 +2569,135 @@ void main() {
expect
(
tester
.
getSize
(
find
.
widgetWithText
(
menuItemType
,
'Item 0'
)).
height
,
48
);
expect
(
tester
.
getSize
(
find
.
widgetWithText
(
menuItemType
,
'Item 0'
)).
width
,
500
);
});
testWidgets
(
'Can change menu position and offset'
,
(
WidgetTester
tester
)
async
{
PopupMenuButton
<
int
>
buildMenuButton
({
required
PopupMenuPosition
position
})
{
return
PopupMenuButton
<
int
>(
position:
position
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuItem
<
int
>>[
PopupMenuItem
<
int
>(
value:
1
,
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
const
Text
(
'AAA'
);
},
),
),
];
},
);
}
// Popup menu with `MenuPosition.over (default) with default offset`.
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Material
(
child:
buildMenuButton
(
position:
PopupMenuPosition
.
over
),
),
),
),
);
// Open the popup menu.
await
tester
.
tap
(
find
.
byType
(
IconButton
));
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getTopLeft
(
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_PopupMenu<int?>'
)),
const
Offset
(
8.0
,
8.0
));
// Close the popup menu.
await
tester
.
tapAt
(
Offset
.
zero
);
await
tester
.
pump
();
// Popup menu with `MenuPosition.under`(custom) with default offset`.
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Material
(
child:
buildMenuButton
(
position:
PopupMenuPosition
.
under
),
),
),
),
);
// Open the popup menu.
await
tester
.
tap
(
find
.
byType
(
IconButton
));
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getTopLeft
(
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_PopupMenu<int?>'
)),
const
Offset
(
8.0
,
40.0
));
// Close the popup menu.
await
tester
.
tapAt
(
Offset
.
zero
);
await
tester
.
pump
();
// Popup menu with `MenuPosition.over (default) with custom offset`.
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Material
(
child:
PopupMenuButton
<
int
>(
offset:
const
Offset
(
0.0
,
50
),
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuItem
<
int
>>[
PopupMenuItem
<
int
>(
value:
1
,
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
const
Text
(
'AAA'
);
},
),
),
];
},
),
),
),
),
);
// Open the popup menu.
await
tester
.
tap
(
find
.
byType
(
IconButton
));
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getTopLeft
(
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_PopupMenu<int?>'
)),
const
Offset
(
8.0
,
50.0
));
// Close the popup menu.
await
tester
.
tapAt
(
Offset
.
zero
);
await
tester
.
pump
();
// Popup menu with `MenuPosition.under (custom) with custom offset`.
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Material
(
child:
PopupMenuButton
<
int
>(
offset:
const
Offset
(
0.0
,
50
),
position:
PopupMenuPosition
.
under
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuItem
<
int
>>[
PopupMenuItem
<
int
>(
value:
1
,
child:
Builder
(
builder:
(
BuildContext
context
)
{
return
const
Text
(
'AAA'
);
},
),
),
];
},
),
),
),
),
);
// Open the popup menu.
await
tester
.
tap
(
find
.
byType
(
IconButton
));
await
tester
.
pumpAndSettle
();
expect
(
tester
.
getTopLeft
(
find
.
byWidgetPredicate
((
Widget
w
)
=>
'
${w.runtimeType}
'
==
'_PopupMenu<int?>'
)),
const
Offset
(
8.0
,
90.0
));
});
}
class
TestApp
extends
StatefulWidget
{
...
...
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