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
b4058b95
Unverified
Commit
b4058b95
authored
Oct 24, 2022
by
Taha Tesser
Committed by
GitHub
Oct 24, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update Popup Menu to support Material 3 (#103606)
parent
700b449d
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
636 additions
and
141 deletions
+636
-141
gen_defaults.dart
dev/tools/gen_defaults/bin/gen_defaults.dart
+2
-0
popup_menu_template.dart
dev/tools/gen_defaults/lib/popup_menu_template.dart
+46
-0
popup_menu.dart
packages/flutter/lib/src/material/popup_menu.dart
+119
-7
popup_menu_theme.dart
packages/flutter/lib/src/material/popup_menu_theme.dart
+32
-1
popup_menu_theme_test.dart
packages/flutter/test/material/popup_menu_theme_test.dart
+437
-133
No files found.
dev/tools/gen_defaults/bin/gen_defaults.dart
View file @
b4058b95
...
...
@@ -33,6 +33,7 @@ import 'package:gen_defaults/input_chip_template.dart';
import
'package:gen_defaults/input_decorator_template.dart'
;
import
'package:gen_defaults/navigation_bar_template.dart'
;
import
'package:gen_defaults/navigation_rail_template.dart'
;
import
'package:gen_defaults/popup_menu_template.dart'
;
import
'package:gen_defaults/progress_indicator_template.dart'
;
import
'package:gen_defaults/radio_template.dart'
;
import
'package:gen_defaults/surface_tint.dart'
;
...
...
@@ -136,6 +137,7 @@ Future<void> main(List<String> args) async {
InputDecoratorTemplate
(
'InputDecorator'
,
'
$materialLib
/input_decorator.dart'
,
tokens
).
updateFile
();
NavigationBarTemplate
(
'NavigationBar'
,
'
$materialLib
/navigation_bar.dart'
,
tokens
).
updateFile
();
NavigationRailTemplate
(
'NavigationRail'
,
'
$materialLib
/navigation_rail.dart'
,
tokens
).
updateFile
();
PopupMenuTemplate
(
'PopupMenu'
,
'
$materialLib
/popup_menu.dart'
,
tokens
).
updateFile
();
ProgressIndicatorTemplate
(
'ProgressIndicator'
,
'
$materialLib
/progress_indicator.dart'
,
tokens
).
updateFile
();
RadioTemplate
(
'Radio<T>'
,
'
$materialLib
/radio.dart'
,
tokens
).
updateFile
();
SurfaceTintTemplate
(
'SurfaceTint'
,
'
$materialLib
/elevation_overlay.dart'
,
tokens
).
updateFile
();
...
...
dev/tools/gen_defaults/lib/popup_menu_template.dart
0 → 100644
View file @
b4058b95
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import
'template.dart'
;
class
PopupMenuTemplate
extends
TokenTemplate
{
const
PopupMenuTemplate
(
super
.
blockName
,
super
.
fileName
,
super
.
tokens
,
{
super
.
colorSchemePrefix
=
'_colors.'
,
super
.
textThemePrefix
=
'_textTheme.'
,
});
@override
String
generate
()
=>
'''
class _
${blockName}
DefaultsM3 extends PopupMenuThemeData {
_
${blockName}
DefaultsM3(this.context)
: super(elevation:
${elevation('md.comp.menu.container')}
);
final BuildContext context;
late final ThemeData _theme = Theme.of(context);
late final ColorScheme _colors = _theme.colorScheme;
late final TextTheme _textTheme = _theme.textTheme;
@override MaterialStateProperty<TextStyle?>? get labelTextStyle {
return MaterialStateProperty.resolveWith((Set<MaterialState> states) {
final TextStyle style = _textTheme.labelLarge!;
if (states.contains(MaterialState.disabled)) {
return style.apply(color:
${componentColor('md.comp.menu.list-item.disabled.label-text')}
);
}
return style.apply(color:
${componentColor('md.comp.menu.list-item.label-text')}
);
});
}
@override
Color? get color =>
${componentColor('md.comp.menu.container')}
;
@override
Color? get shadowColor =>
${color("md.comp.menu.container.shadow-color")}
;
@override
Color? get surfaceTintColor =>
${color("md.comp.menu.container.surface-tint-layer.color")}
;
@override
ShapeBorder? get shape =>
${shape("md.comp.menu.container")}
;
}'''
;
}
packages/flutter/lib/src/material/popup_menu.dart
View file @
b4058b95
...
...
@@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter/widgets.dart'
;
import
'color_scheme.dart'
;
import
'constants.dart'
;
import
'debug.dart'
;
import
'divider.dart'
;
...
...
@@ -17,6 +18,7 @@ import 'material.dart';
import
'material_localizations.dart'
;
import
'material_state.dart'
;
import
'popup_menu_theme.dart'
;
import
'text_theme.dart'
;
import
'theme.dart'
;
import
'tooltip.dart'
;
...
...
@@ -224,6 +226,7 @@ class PopupMenuItem<T> extends PopupMenuEntry<T> {
this
.
height
=
kMinInteractiveDimension
,
this
.
padding
,
this
.
textStyle
,
this
.
labelTextStyle
,
this
.
mouseCursor
,
required
this
.
child
,
})
:
assert
(
enabled
!=
null
),
...
...
@@ -263,6 +266,16 @@ class PopupMenuItem<T> extends PopupMenuEntry<T> {
/// of [ThemeData.textTheme] is used.
final
TextStyle
?
textStyle
;
/// The label style of the popup menu item.
///
/// When [ThemeData.useMaterial3] is true, this styles the text of the popup menu item.
///
/// If this property is null, then [PopupMenuThemeData.labelTextStyle] is used.
/// If [PopupMenuThemeData.labelTextStyle] is also null, then [TextTheme.labelLarge]
/// is used with the [ColorScheme.onSurface] color when popup menu item is enabled and
/// the [ColorScheme.onSurface] color with 0.38 opacity when the popup menu item is disabled.
final
MaterialStateProperty
<
TextStyle
?>?
labelTextStyle
;
/// {@template flutter.material.popupmenu.mouseCursor}
/// The cursor for a mouse pointer when it enters or is hovering over the
/// widget.
...
...
@@ -336,9 +349,20 @@ class PopupMenuItemState<T, W extends PopupMenuItem<T>> extends State<W> {
Widget
build
(
BuildContext
context
)
{
final
ThemeData
theme
=
Theme
.
of
(
context
);
final
PopupMenuThemeData
popupMenuTheme
=
PopupMenuTheme
.
of
(
context
);
TextStyle
style
=
widget
.
textStyle
??
popupMenuTheme
.
textStyle
??
theme
.
textTheme
.
titleMedium
!;
if
(!
widget
.
enabled
)
{
final
PopupMenuThemeData
defaults
=
theme
.
useMaterial3
?
_PopupMenuDefaultsM3
(
context
)
:
_PopupMenuDefaultsM2
(
context
);
final
Set
<
MaterialState
>
states
=
<
MaterialState
>{
if
(!
widget
.
enabled
)
MaterialState
.
disabled
,
};
TextStyle
style
=
theme
.
useMaterial3
?
(
widget
.
labelTextStyle
?.
resolve
(
states
)
??
popupMenuTheme
.
labelTextStyle
?.
resolve
(
states
)!
??
defaults
.
labelTextStyle
!.
resolve
(
states
)!)
:
(
widget
.
textStyle
??
popupMenuTheme
.
textStyle
??
defaults
.
textStyle
!);
if
(!
widget
.
enabled
&&
!
theme
.
useMaterial3
)
{
style
=
style
.
copyWith
(
color:
theme
.
disabledColor
);
}
...
...
@@ -537,7 +561,9 @@ class _PopupMenu<T> extends StatelessWidget {
Widget
build
(
BuildContext
context
)
{
final
double
unit
=
1.0
/
(
route
.
items
.
length
+
1.5
);
// 1.0 for the width and 0.5 for the last item's fade.
final
List
<
Widget
>
children
=
<
Widget
>[];
final
ThemeData
theme
=
Theme
.
of
(
context
);
final
PopupMenuThemeData
popupMenuTheme
=
PopupMenuTheme
.
of
(
context
);
final
PopupMenuThemeData
defaults
=
theme
.
useMaterial3
?
_PopupMenuDefaultsM3
(
context
)
:
_PopupMenuDefaultsM2
(
context
);
for
(
int
i
=
0
;
i
<
route
.
items
.
length
;
i
+=
1
)
{
final
double
start
=
(
i
+
1
)
*
unit
;
...
...
@@ -598,11 +624,13 @@ class _PopupMenu<T> extends StatelessWidget {
return
FadeTransition
(
opacity:
opacity
.
animate
(
route
.
animation
!),
child:
Material
(
shape:
route
.
shape
??
popupMenuTheme
.
shape
,
color:
route
.
color
??
popupMenuTheme
.
color
,
shape:
route
.
shape
??
popupMenuTheme
.
shape
??
defaults
.
shape
,
color:
route
.
color
??
popupMenuTheme
.
color
??
defaults
.
color
,
clipBehavior:
clipBehavior
,
type:
MaterialType
.
card
,
elevation:
route
.
elevation
??
popupMenuTheme
.
elevation
??
8.0
,
elevation:
route
.
elevation
??
popupMenuTheme
.
elevation
??
defaults
.
elevation
!,
shadowColor:
route
.
shadowColor
??
popupMenuTheme
.
shadowColor
??
defaults
.
shadowColor
,
surfaceTintColor:
route
.
surfaceTintColor
??
popupMenuTheme
.
surfaceTintColor
??
defaults
.
surfaceTintColor
,
child:
Align
(
alignment:
AlignmentDirectional
.
topEnd
,
widthFactor:
width
.
evaluate
(
route
.
animation
!),
...
...
@@ -757,6 +785,8 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
required
this
.
items
,
this
.
initialValue
,
this
.
elevation
,
this
.
surfaceTintColor
,
this
.
shadowColor
,
required
this
.
barrierLabel
,
this
.
semanticLabel
,
this
.
shape
,
...
...
@@ -771,6 +801,8 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
final
List
<
Size
?>
itemSizes
;
final
T
?
initialValue
;
final
double
?
elevation
;
final
Color
?
surfaceTintColor
;
final
Color
?
shadowColor
;
final
String
?
semanticLabel
;
final
ShapeBorder
?
shape
;
final
Color
?
color
;
...
...
@@ -911,6 +943,8 @@ Future<T?> showMenu<T>({
required
List
<
PopupMenuEntry
<
T
>>
items
,
T
?
initialValue
,
double
?
elevation
,
Color
?
shadowColor
,
Color
?
surfaceTintColor
,
String
?
semanticLabel
,
ShapeBorder
?
shape
,
Color
?
color
,
...
...
@@ -941,6 +975,8 @@ Future<T?> showMenu<T>({
items:
items
,
initialValue:
initialValue
,
elevation:
elevation
,
shadowColor:
shadowColor
,
surfaceTintColor:
surfaceTintColor
,
semanticLabel:
semanticLabel
,
barrierLabel:
MaterialLocalizations
.
of
(
context
).
modalBarrierDismissLabel
,
shape:
shape
,
...
...
@@ -1006,6 +1042,8 @@ class PopupMenuButton<T> extends StatefulWidget {
this
.
onCanceled
,
this
.
tooltip
,
this
.
elevation
,
this
.
shadowColor
,
this
.
surfaceTintColor
,
this
.
padding
=
const
EdgeInsets
.
all
(
8.0
),
this
.
child
,
this
.
splashRadius
,
...
...
@@ -1058,6 +1096,22 @@ class PopupMenuButton<T> extends StatefulWidget {
/// Defaults to 8, the appropriate elevation for popup menus.
final
double
?
elevation
;
/// The color used to paint the shadow below the menu.
///
/// If null then the ambient [PopupMenuThemeData.shadowColor] is used.
/// If that is null too, then the overall theme's [ThemeData.shadowColor]
/// (default black) is used.
final
Color
?
shadowColor
;
/// The color used as an overlay on [color] to indicate elevation.
///
/// If null, [PopupMenuThemeData.surfaceTintColor] is used. If that
/// is also null, the default value is [ColorScheme.surfaceTint].
///
/// See [Material.surfaceTintColor] for more details on how this
/// overlay is applied.
final
Color
?
surfaceTintColor
;
/// Matches IconButton's 8 dps padding by default. In some cases, notably where
/// this button appears as the trailing element of a list item, it's useful to be able
/// to set the padding to zero.
...
...
@@ -1207,6 +1261,8 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
showMenu
<
T
?>(
context:
context
,
elevation:
widget
.
elevation
??
popupMenuTheme
.
elevation
,
shadowColor:
widget
.
shadowColor
??
popupMenuTheme
.
shadowColor
,
surfaceTintColor:
widget
.
surfaceTintColor
??
popupMenuTheme
.
surfaceTintColor
,
items:
items
,
initialValue:
widget
.
initialValue
,
position:
position
,
...
...
@@ -1240,6 +1296,7 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
@override
Widget
build
(
BuildContext
context
)
{
final
IconThemeData
iconTheme
=
IconTheme
.
of
(
context
);
final
bool
enableFeedback
=
widget
.
enableFeedback
??
PopupMenuTheme
.
of
(
context
).
enableFeedback
??
true
;
...
...
@@ -1263,7 +1320,8 @@ class PopupMenuButtonState<T> extends State<PopupMenuButton<T>> {
icon:
widget
.
icon
??
Icon
(
Icons
.
adaptive
.
more
),
padding:
widget
.
padding
,
splashRadius:
widget
.
splashRadius
,
iconSize:
widget
.
iconSize
,
iconSize:
widget
.
iconSize
??
iconTheme
.
size
,
color:
widget
.
color
??
iconTheme
.
color
,
tooltip:
widget
.
tooltip
??
MaterialLocalizations
.
of
(
context
).
showMenuTooltip
,
onPressed:
widget
.
enabled
?
showButtonMenu
:
null
,
enableFeedback:
enableFeedback
,
...
...
@@ -1290,3 +1348,57 @@ class _EffectiveMouseCursor extends MaterialStateMouseCursor {
@override
String
get
debugDescription
=>
'MaterialStateMouseCursor(PopupMenuItemState)'
;
}
class
_PopupMenuDefaultsM2
extends
PopupMenuThemeData
{
_PopupMenuDefaultsM2
(
this
.
context
)
:
super
(
elevation:
8.0
);
final
BuildContext
context
;
late
final
ThemeData
_theme
=
Theme
.
of
(
context
);
late
final
TextTheme
_textTheme
=
_theme
.
textTheme
;
@override
TextStyle
?
get
textStyle
=>
_textTheme
.
subtitle1
;
}
// BEGIN GENERATED TOKEN PROPERTIES - PopupMenu
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
// dev/tools/gen_defaults/bin/gen_defaults.dart.
// Token database version: v0_132
class
_PopupMenuDefaultsM3
extends
PopupMenuThemeData
{
_PopupMenuDefaultsM3
(
this
.
context
)
:
super
(
elevation:
3.0
);
final
BuildContext
context
;
late
final
ThemeData
_theme
=
Theme
.
of
(
context
);
late
final
ColorScheme
_colors
=
_theme
.
colorScheme
;
late
final
TextTheme
_textTheme
=
_theme
.
textTheme
;
@override
MaterialStateProperty
<
TextStyle
?>?
get
labelTextStyle
{
return
MaterialStateProperty
.
resolveWith
((
Set
<
MaterialState
>
states
)
{
final
TextStyle
style
=
_textTheme
.
labelLarge
!;
if
(
states
.
contains
(
MaterialState
.
disabled
))
{
return
style
.
apply
(
color:
_colors
.
onSurface
.
withOpacity
(
0.38
));
}
return
style
.
apply
(
color:
_colors
.
onSurface
);
});
}
@override
Color
?
get
color
=>
_colors
.
surface
;
@override
Color
?
get
shadowColor
=>
_colors
.
shadow
;
@override
Color
?
get
surfaceTintColor
=>
_colors
.
surfaceTint
;
@override
ShapeBorder
?
get
shape
=>
const
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
4.0
)));
}
// END GENERATED TOKEN PROPERTIES - PopupMenu
packages/flutter/lib/src/material/popup_menu_theme.dart
View file @
b4058b95
...
...
@@ -48,7 +48,10 @@ class PopupMenuThemeData with Diagnosticable {
this
.
color
,
this
.
shape
,
this
.
elevation
,
this
.
shadowColor
,
this
.
surfaceTintColor
,
this
.
textStyle
,
this
.
labelTextStyle
,
this
.
enableFeedback
,
this
.
mouseCursor
,
this
.
position
,
...
...
@@ -63,9 +66,19 @@ class PopupMenuThemeData with Diagnosticable {
/// The elevation of the popup menu.
final
double
?
elevation
;
/// The color used to paint shadow below the popup menu.
final
Color
?
shadowColor
;
/// The color used as an overlay on [color] of the popup menu.
final
Color
?
surfaceTintColor
;
/// The text style of items in the popup menu.
final
TextStyle
?
textStyle
;
/// You can use this to specify a different style of the label
/// when the popup menu item is enabled and disabled.
final
MaterialStateProperty
<
TextStyle
?>?
labelTextStyle
;
/// If specified, defines the feedback property for [PopupMenuButton].
///
/// If [PopupMenuButton.enableFeedback] is provided, [enableFeedback] is ignored.
...
...
@@ -88,7 +101,10 @@ class PopupMenuThemeData with Diagnosticable {
Color
?
color
,
ShapeBorder
?
shape
,
double
?
elevation
,
Color
?
shadowColor
,
Color
?
surfaceTintColor
,
TextStyle
?
textStyle
,
MaterialStateProperty
<
TextStyle
?>?
labelTextStyle
,
bool
?
enableFeedback
,
MaterialStateProperty
<
MouseCursor
?>?
mouseCursor
,
PopupMenuPosition
?
position
,
...
...
@@ -97,7 +113,10 @@ class PopupMenuThemeData with Diagnosticable {
color:
color
??
this
.
color
,
shape:
shape
??
this
.
shape
,
elevation:
elevation
??
this
.
elevation
,
shadowColor:
shadowColor
??
this
.
shadowColor
,
surfaceTintColor:
surfaceTintColor
??
this
.
surfaceTintColor
,
textStyle:
textStyle
??
this
.
textStyle
,
labelTextStyle:
labelTextStyle
??
this
.
labelTextStyle
,
enableFeedback:
enableFeedback
??
this
.
enableFeedback
,
mouseCursor:
mouseCursor
??
this
.
mouseCursor
,
position:
position
??
this
.
position
,
...
...
@@ -118,7 +137,10 @@ class PopupMenuThemeData with Diagnosticable {
color:
Color
.
lerp
(
a
?.
color
,
b
?.
color
,
t
),
shape:
ShapeBorder
.
lerp
(
a
?.
shape
,
b
?.
shape
,
t
),
elevation:
lerpDouble
(
a
?.
elevation
,
b
?.
elevation
,
t
),
shadowColor:
Color
.
lerp
(
a
?.
shadowColor
,
b
?.
shadowColor
,
t
),
surfaceTintColor:
Color
.
lerp
(
a
?.
surfaceTintColor
,
b
?.
surfaceTintColor
,
t
),
textStyle:
TextStyle
.
lerp
(
a
?.
textStyle
,
b
?.
textStyle
,
t
),
labelTextStyle:
MaterialStateProperty
.
lerp
<
TextStyle
?>(
a
?.
labelTextStyle
,
b
?.
labelTextStyle
,
t
,
TextStyle
.
lerp
),
enableFeedback:
t
<
0.5
?
a
?.
enableFeedback
:
b
?.
enableFeedback
,
mouseCursor:
t
<
0.5
?
a
?.
mouseCursor
:
b
?.
mouseCursor
,
position:
t
<
0.5
?
a
?.
position
:
b
?.
position
,
...
...
@@ -130,7 +152,10 @@ class PopupMenuThemeData with Diagnosticable {
color
,
shape
,
elevation
,
shadowColor
,
surfaceTintColor
,
textStyle
,
labelTextStyle
,
enableFeedback
,
mouseCursor
,
position
,
...
...
@@ -145,10 +170,13 @@ class PopupMenuThemeData with Diagnosticable {
return
false
;
}
return
other
is
PopupMenuThemeData
&&
other
.
elevation
==
elevation
&&
other
.
color
==
color
&&
other
.
shape
==
shape
&&
other
.
elevation
==
elevation
&&
other
.
shadowColor
==
shadowColor
&&
other
.
surfaceTintColor
==
surfaceTintColor
&&
other
.
textStyle
==
textStyle
&&
other
.
labelTextStyle
==
labelTextStyle
&&
other
.
enableFeedback
==
enableFeedback
&&
other
.
mouseCursor
==
mouseCursor
&&
other
.
position
==
position
;
...
...
@@ -160,7 +188,10 @@ class PopupMenuThemeData with Diagnosticable {
properties
.
add
(
ColorProperty
(
'color'
,
color
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
ShapeBorder
>(
'shape'
,
shape
,
defaultValue:
null
));
properties
.
add
(
DoubleProperty
(
'elevation'
,
elevation
,
defaultValue:
null
));
properties
.
add
(
ColorProperty
(
'shadowColor'
,
shadowColor
,
defaultValue:
null
));
properties
.
add
(
ColorProperty
(
'surfaceTintColor'
,
surfaceTintColor
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
TextStyle
>(
'text style'
,
textStyle
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
MaterialStateProperty
<
TextStyle
?>>(
'labelTextStyle'
,
labelTextStyle
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
bool
>(
'enableFeedback'
,
enableFeedback
,
defaultValue:
null
));
properties
.
add
(
DiagnosticsProperty
<
MaterialStateProperty
<
MouseCursor
?>>(
'mouseCursor'
,
mouseCursor
,
defaultValue:
null
));
properties
.
add
(
EnumProperty
<
PopupMenuPosition
?>(
'position'
,
position
,
defaultValue:
null
));
...
...
packages/flutter/test/material/popup_menu_theme_test.dart
View file @
b4058b95
...
...
@@ -7,13 +7,40 @@ import 'package:flutter/material.dart';
import
'package:flutter/rendering.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
PopupMenuThemeData
_popupMenuTheme
(
)
{
return
const
PopupMenuThemeData
(
PopupMenuThemeData
_popupMenuTheme
M2
(
)
{
return
PopupMenuThemeData
(
color:
Colors
.
orange
,
shape:
BeveledRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
12
))),
shape:
const
BeveledRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
12
))),
elevation:
12.0
,
textStyle:
TextStyle
(
color:
Color
(
0xffffffff
),
textBaseline:
TextBaseline
.
alphabetic
),
position:
PopupMenuPosition
.
under
,
textStyle:
const
TextStyle
(
color:
Color
(
0xffffffff
),
textBaseline:
TextBaseline
.
alphabetic
),
mouseCursor:
MaterialStateProperty
.
resolveWith
<
MouseCursor
?>((
Set
<
MaterialState
>
states
)
{
if
(
states
.
contains
(
MaterialState
.
disabled
))
{
return
SystemMouseCursors
.
contextMenu
;
}
return
SystemMouseCursors
.
alias
;
}),
);
}
PopupMenuThemeData
_popupMenuThemeM3
(
)
{
return
PopupMenuThemeData
(
color:
Colors
.
orange
,
shape:
const
BeveledRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
12
))),
elevation:
12.0
,
shadowColor:
const
Color
(
0xff00ff00
),
surfaceTintColor:
const
Color
(
0xff00ff00
),
labelTextStyle:
MaterialStateProperty
.
resolveWith
((
Set
<
MaterialState
>
states
)
{
if
(
states
.
contains
(
MaterialState
.
disabled
))
{
return
const
TextStyle
(
color:
Color
(
0xfff99ff0
),
fontSize:
12.0
);
}
return
const
TextStyle
(
color:
Color
(
0xfff12099
),
fontSize:
17.0
);
}),
mouseCursor:
MaterialStateProperty
.
resolveWith
<
MouseCursor
?>((
Set
<
MaterialState
>
states
)
{
if
(
states
.
contains
(
MaterialState
.
disabled
))
{
return
SystemMouseCursors
.
contextMenu
;
}
return
SystemMouseCursors
.
alias
;
}),
);
}
...
...
@@ -28,9 +55,12 @@ void main() {
expect
(
popupMenuTheme
.
color
,
null
);
expect
(
popupMenuTheme
.
shape
,
null
);
expect
(
popupMenuTheme
.
elevation
,
null
);
expect
(
popupMenuTheme
.
shadowColor
,
null
);
expect
(
popupMenuTheme
.
surfaceTintColor
,
null
);
expect
(
popupMenuTheme
.
textStyle
,
null
);
expect
(
popupMenuTheme
.
labelTextStyle
,
null
);
expect
(
popupMenuTheme
.
enableFeedback
,
null
);
expect
(
popupMenuTheme
.
mouseCursor
,
null
);
expect
(
popupMenuTheme
.
position
,
null
);
});
testWidgets
(
'Default PopupMenuThemeData debugFillProperties'
,
(
WidgetTester
tester
)
async
{
...
...
@@ -47,13 +77,20 @@ void main() {
testWidgets
(
'PopupMenuThemeData implements debugFillProperties'
,
(
WidgetTester
tester
)
async
{
final
DiagnosticPropertiesBuilder
builder
=
DiagnosticPropertiesBuilder
();
const
PopupMenuThemeData
(
color:
Color
(
0xFFFFFFFF
),
shape:
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
2.0
))),
PopupMenuThemeData
(
color:
const
Color
(
0xFFFFFFFF
),
shape:
const
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
2.0
))),
elevation:
2.0
,
textStyle:
TextStyle
(
color:
Color
(
0xffffffff
)),
shadowColor:
const
Color
(
0xff00ff00
),
surfaceTintColor:
const
Color
(
0xff00ff00
),
textStyle:
const
TextStyle
(
color:
Color
(
0xffffffff
)),
labelTextStyle:
MaterialStateProperty
.
resolveWith
((
Set
<
MaterialState
>
states
)
{
if
(
states
.
contains
(
MaterialState
.
disabled
))
{
return
const
TextStyle
(
color:
Color
(
0xfff99ff0
),
fontSize:
12.0
);
}
return
const
TextStyle
(
color:
Color
(
0xfff12099
),
fontSize:
17.0
);
}),
mouseCursor:
MaterialStateMouseCursor
.
clickable
,
position:
PopupMenuPosition
.
over
,
).
debugFillProperties
(
builder
);
final
List
<
String
>
description
=
builder
.
properties
...
...
@@ -65,19 +102,23 @@ void main() {
'color: Color(0xffffffff)'
,
'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.circular(2.0))'
,
'elevation: 2.0'
,
'shadowColor: Color(0xff00ff00)'
,
'surfaceTintColor: Color(0xff00ff00)'
,
'text style: TextStyle(inherit: true, color: Color(0xffffffff))'
,
"labelTextStyle: Instance of '_MaterialStatePropertyWith<TextStyle?>'"
,
'mouseCursor: MaterialStateMouseCursor(clickable)'
,
'position: over'
]);
});
testWidgets
(
'Passing no PopupMenuThemeData returns defaults'
,
(
WidgetTester
tester
)
async
{
final
Key
popupButtonKey
=
UniqueKey
();
final
Key
popupButtonApp
=
UniqueKey
();
final
Key
popupItemKey
=
UniqueKey
();
final
Key
enabledPopupItemKey
=
UniqueKey
();
final
Key
disabledPopupItemKey
=
UniqueKey
();
final
ThemeData
theme
=
ThemeData
(
useMaterial3:
true
);
await
tester
.
pumpWidget
(
MaterialApp
(
theme:
ThemeData
()
,
theme:
theme
,
key:
popupButtonApp
,
home:
Material
(
child:
Column
(
...
...
@@ -91,8 +132,14 @@ void main() {
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuEntry
<
void
>>[
PopupMenuItem
<
void
>(
key:
popupItemKey
,
child:
const
Text
(
'Example'
),
key:
enabledPopupItemKey
,
child:
const
Text
(
'Enabled PopupMenuItem'
),
),
const
PopupMenuDivider
(),
PopupMenuItem
<
void
>(
key:
disabledPopupItemKey
,
enabled:
false
,
child:
const
Text
(
'Disabled PopupMenuItem'
),
),
];
},
...
...
@@ -116,37 +163,63 @@ void main() {
matching:
find
.
byType
(
Material
),
).
last
,
);
expect
(
button
.
color
,
null
);
expect
(
button
.
shape
,
null
);
expect
(
button
.
elevation
,
8.0
);
expect
(
button
.
color
,
theme
.
colorScheme
.
surface
);
expect
(
button
.
shadowColor
,
theme
.
colorScheme
.
shadow
);
expect
(
button
.
surfaceTintColor
,
theme
.
colorScheme
.
surfaceTint
);
expect
(
button
.
shape
,
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
circular
(
4.0
)));
expect
(
button
.
elevation
,
3.0
);
/// The last DefaultTextStyle widget under popupItemKey is the
/// [PopupMenuItem] specified above, so by finding the last descendent of
/// popupItemKey that is of type DefaultTextStyle, this code retrieves the
/// built [PopupMenuItem].
final
DefaultTextStyle
t
ext
=
tester
.
widget
<
DefaultTextStyle
>(
final
DefaultTextStyle
enabledT
ext
=
tester
.
widget
<
DefaultTextStyle
>(
find
.
descendant
(
of:
find
.
byKey
(
popupItemKey
),
of:
find
.
byKey
(
enabledPopupItemKey
),
matching:
find
.
byType
(
DefaultTextStyle
),
).
last
,
);
expect
(
enabledText
.
style
.
fontFamily
,
'Roboto'
);
expect
(
enabledText
.
style
.
color
,
const
Color
(
0xff000000
));
/// Test disabled text color
final
DefaultTextStyle
disabledText
=
tester
.
widget
<
DefaultTextStyle
>(
find
.
descendant
(
of:
find
.
byKey
(
disabledPopupItemKey
),
matching:
find
.
byType
(
DefaultTextStyle
),
).
last
,
);
expect
(
text
.
style
.
fontFamily
,
'Roboto'
);
expect
(
text
.
style
.
color
,
const
Color
(
0xdd000000
));
expect
(
text
.
style
.
color
,
const
Color
(
0xdd000000
));
expect
(
disabledText
.
style
.
color
,
theme
.
colorScheme
.
onSurface
.
withOpacity
(
0.38
));
final
Offset
topLeftButton
=
tester
.
getTopLeft
(
find
.
byType
(
PopupMenuButton
<
void
>));
final
Offset
topLeftMenu
=
tester
.
getTopLeft
(
find
.
byWidget
(
button
));
expect
(
topLeftMenu
,
topLeftButton
);
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
();
addTearDown
(
gesture
.
removePointer
);
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byKey
(
disabledPopupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
basic
,
);
await
gesture
.
down
(
tester
.
getCenter
(
find
.
byKey
(
enabledPopupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
click
,
);
});
testWidgets
(
'Popup menu uses values from PopupMenuThemeData'
,
(
WidgetTester
tester
)
async
{
final
PopupMenuThemeData
popupMenuTheme
=
_popupMenuTheme
();
final
PopupMenuThemeData
popupMenuTheme
=
_popupMenuTheme
M3
();
final
Key
popupButtonKey
=
UniqueKey
();
final
Key
popupButtonApp
=
UniqueKey
();
final
Key
popupItemKey
=
UniqueKey
();
final
Key
enabledPopupItemKey
=
UniqueKey
();
final
Key
disabledPopupItemKey
=
UniqueKey
();
await
tester
.
pumpWidget
(
MaterialApp
(
theme:
ThemeData
(
popupMenuTheme:
popupMenuTheme
),
theme:
ThemeData
(
useMaterial3:
true
,
popupMenuTheme:
popupMenuTheme
),
key:
popupButtonApp
,
home:
Material
(
child:
Column
(
...
...
@@ -160,8 +233,15 @@ void main() {
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuEntry
<
Object
>>[
PopupMenuItem
<
Object
>(
key:
popupItemKey
,
child:
const
Text
(
'Example'
),
key:
disabledPopupItemKey
,
enabled:
false
,
child:
const
Text
(
'disabled'
),
),
const
PopupMenuDivider
(),
PopupMenuItem
<
Object
>(
key:
enabledPopupItemKey
,
onTap:
()
{
},
child:
const
Text
(
'enabled'
),
),
];
},
...
...
@@ -184,66 +264,90 @@ void main() {
matching:
find
.
byType
(
Material
),
).
last
,
);
expect
(
button
.
color
,
popupMenuTheme
.
color
);
expect
(
button
.
shape
,
popupMenuTheme
.
shape
);
expect
(
button
.
elevation
,
popupMenuTheme
.
elevation
);
expect
(
button
.
color
,
Colors
.
orange
);
expect
(
button
.
surfaceTintColor
,
const
Color
(
0xff00ff00
));
expect
(
button
.
shadowColor
,
const
Color
(
0xff00ff00
));
expect
(
button
.
shape
,
const
BeveledRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
12
))));
expect
(
button
.
elevation
,
12.0
);
/// The last DefaultTextStyle widget under popupItemKey is the
/// [PopupMenuItem] specified above, so by finding the last descendent of
/// popupItemKey that is of type DefaultTextStyle, this code retrieves the
/// built [PopupMenuItem].
final
DefaultTextStyle
text
=
tester
.
widget
<
DefaultTextStyle
>(
final
DefaultTextStyle
enabledText
=
tester
.
widget
<
DefaultTextStyle
>(
find
.
descendant
(
of:
find
.
byKey
(
popupItemKey
),
of:
find
.
byKey
(
enabledPopupItemKey
),
matching:
find
.
byType
(
DefaultTextStyle
),
).
last
,
);
expect
(
enabledText
.
style
,
popupMenuTheme
.
labelTextStyle
?.
resolve
(
enabled
),
);
/// Test disabled text color
final
DefaultTextStyle
disabledText
=
tester
.
widget
<
DefaultTextStyle
>(
find
.
descendant
(
of:
find
.
byKey
(
disabledPopupItemKey
),
matching:
find
.
byType
(
DefaultTextStyle
),
).
last
,
);
expect
(
text
.
style
,
popupMenuTheme
.
textStyle
);
expect
(
disabledText
.
style
,
popupMenuTheme
.
labelTextStyle
?.
resolve
(
disabled
),
);
final
Offset
bottomLeftButton
=
tester
.
getBottomLeft
(
find
.
byType
(
PopupMenuButton
<
void
>));
final
Offset
topLeftMenu
=
tester
.
getTopLeft
(
find
.
byWidget
(
button
));
expect
(
topLeftMenu
,
bottomLeftButton
);
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
();
addTearDown
(
gesture
.
removePointer
);
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byKey
(
disabledPopupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
popupMenuTheme
.
mouseCursor
?.
resolve
(
disabled
),
);
await
gesture
.
down
(
tester
.
getCenter
(
find
.
byKey
(
enabledPopupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
popupMenuTheme
.
mouseCursor
?.
resolve
(
enabled
),
);
});
testWidgets
(
'Popup menu widget properties take priority over theme'
,
(
WidgetTester
tester
)
async
{
final
PopupMenuThemeData
popupMenuTheme
=
_popupMenuTheme
();
final
PopupMenuThemeData
popupMenuTheme
=
_popupMenuTheme
M3
();
final
Key
popupButtonKey
=
UniqueKey
();
final
Key
popupButtonApp
=
UniqueKey
();
final
Key
popupItemKey
=
UniqueKey
();
const
Color
color
=
Colors
.
purple
;
const
Color
surfaceTintColor
=
Colors
.
amber
;
const
Color
shadowColor
=
Colors
.
green
;
const
ShapeBorder
shape
=
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
9.0
)),
);
const
double
elevation
=
7.0
;
const
TextStyle
textStyle
=
TextStyle
(
color:
Color
(
0x00000000
),
textBaseline:
TextBaseline
.
alphabetic
);
const
TextStyle
textStyle
=
TextStyle
(
color:
Color
(
0xffffffef
),
fontSize:
19.0
);
const
MouseCursor
cursor
=
SystemMouseCursors
.
forbidden
;
await
tester
.
pumpWidget
(
MaterialApp
(
theme:
ThemeData
(
popupMenuTheme:
popupMenuTheme
),
theme:
ThemeData
(
useMaterial3:
true
,
popupMenuTheme:
popupMenuTheme
),
key:
popupButtonApp
,
home:
Material
(
child:
Column
(
children:
<
Widget
>[
Padding
(
// The padding makes sure the menu has enough space around it to
// get properly aligned when displayed (`_kMenuScreenPadding`).
padding:
const
EdgeInsets
.
all
(
8.0
),
child:
PopupMenuButton
<
void
>(
key:
popupButtonKey
,
elevation:
elevation
,
color:
color
,
shape:
shape
,
position:
PopupMenuPosition
.
over
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuEntry
<
void
>>[
PopupMenuItem
<
void
>(
key:
popupItemKey
,
textStyle:
textStyle
,
child:
const
Text
(
'Example'
),
),
];
},
),
PopupMenuButton
<
void
>(
key:
popupButtonKey
,
elevation:
elevation
,
shadowColor:
shadowColor
,
surfaceTintColor:
surfaceTintColor
,
color:
color
,
shape:
shape
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuEntry
<
void
>>[
PopupMenuItem
<
void
>(
key:
popupItemKey
,
labelTextStyle:
MaterialStateProperty
.
all
<
TextStyle
>(
textStyle
),
mouseCursor:
cursor
,
child:
const
Text
(
'Example'
),
),
];
},
),
],
),
...
...
@@ -266,6 +370,8 @@ void main() {
expect
(
button
.
color
,
color
);
expect
(
button
.
shape
,
shape
);
expect
(
button
.
elevation
,
elevation
);
expect
(
button
.
shadowColor
,
shadowColor
);
expect
(
button
.
surfaceTintColor
,
surfaceTintColor
);
/// The last DefaultTextStyle widget under popupItemKey is the
/// [PopupMenuItem] specified above, so by finding the last descendent of
...
...
@@ -279,45 +385,145 @@ void main() {
);
expect
(
text
.
style
,
textStyle
);
final
Offset
topLeftButton
=
tester
.
getTopLeft
(
find
.
byType
(
PopupMenuButton
<
void
>));
final
Offset
topLeftMenu
=
tester
.
getTopLeft
(
find
.
byWidget
(
button
));
expect
(
topLeftMenu
,
topLeftButton
);
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
();
addTearDown
(
gesture
.
removePointer
);
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byKey
(
popupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
cursor
);
});
testWidgets
(
'ThemeData.popupMenuTheme properties are utilized'
,
(
WidgetTester
tester
)
async
{
final
Key
popupButtonKey
=
UniqueKey
();
final
Key
popupButtonApp
=
UniqueKey
();
final
Key
enabledPopupItemKey
=
UniqueKey
();
final
Key
disabledPopupItemKey
=
UniqueKey
();
await
tester
.
pumpWidget
(
MaterialApp
(
key:
popupButtonApp
,
home:
Material
(
child:
Column
(
children:
<
Widget
>[
PopupMenuTheme
(
data:
PopupMenuThemeData
(
color:
Colors
.
pink
,
shape:
const
BeveledRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
10
))),
elevation:
6.0
,
textStyle:
const
TextStyle
(
color:
Color
(
0xfffff000
),
textBaseline:
TextBaseline
.
alphabetic
),
mouseCursor:
MaterialStateProperty
.
resolveWith
<
MouseCursor
?>((
Set
<
MaterialState
>
states
)
{
if
(
states
.
contains
(
MaterialState
.
disabled
))
{
return
SystemMouseCursors
.
contextMenu
;
}
return
SystemMouseCursors
.
alias
;
}),
group
(
'Material 2'
,
()
{
// Tests that are only relevant for Material 2. Once ThemeData.useMaterial3
// is turned on by default, these tests can be removed.
testWidgets
(
'Passing no PopupMenuThemeData returns defaults'
,
(
WidgetTester
tester
)
async
{
final
Key
popupButtonKey
=
UniqueKey
();
final
Key
popupButtonApp
=
UniqueKey
();
final
Key
enabledPopupItemKey
=
UniqueKey
();
final
Key
disabledPopupItemKey
=
UniqueKey
();
final
ThemeData
theme
=
ThemeData
();
await
tester
.
pumpWidget
(
MaterialApp
(
theme:
theme
,
key:
popupButtonApp
,
home:
Material
(
child:
Column
(
children:
<
Widget
>[
Padding
(
// The padding makes sure the menu has enough space around it to
// get properly aligned when displayed (`_kMenuScreenPadding`).
padding:
const
EdgeInsets
.
all
(
8.0
),
child:
PopupMenuButton
<
void
>(
key:
popupButtonKey
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuEntry
<
void
>>[
PopupMenuItem
<
void
>(
key:
enabledPopupItemKey
,
child:
const
Text
(
'Enabled PopupMenuItem'
),
),
const
PopupMenuDivider
(),
PopupMenuItem
<
void
>(
key:
disabledPopupItemKey
,
enabled:
false
,
child:
const
Text
(
'Disabled PopupMenuItem'
),
),
];
},
),
),
child:
PopupMenuButton
<
void
>(
],
),
),
));
await
tester
.
tap
(
find
.
byKey
(
popupButtonKey
));
await
tester
.
pumpAndSettle
();
/// The last Material widget under popupButtonApp is the [PopupMenuButton]
/// specified above, so by finding the last descendent of popupButtonApp
/// that is of type Material, this code retrieves the built
/// [PopupMenuButton].
final
Material
button
=
tester
.
widget
<
Material
>(
find
.
descendant
(
of:
find
.
byKey
(
popupButtonApp
),
matching:
find
.
byType
(
Material
),
).
last
,
);
expect
(
button
.
color
,
null
);
expect
(
button
.
shape
,
null
);
expect
(
button
.
elevation
,
8.0
);
/// The last DefaultTextStyle widget under popupItemKey is the
/// [PopupMenuItem] specified above, so by finding the last descendent of
/// popupItemKey that is of type DefaultTextStyle, this code retrieves the
/// built [PopupMenuItem].
final
DefaultTextStyle
enabledText
=
tester
.
widget
<
DefaultTextStyle
>(
find
.
descendant
(
of:
find
.
byKey
(
enabledPopupItemKey
),
matching:
find
.
byType
(
DefaultTextStyle
),
).
last
,
);
expect
(
enabledText
.
style
.
fontFamily
,
'Roboto'
);
expect
(
enabledText
.
style
.
color
,
const
Color
(
0xdd000000
));
/// Test disabled text color
final
DefaultTextStyle
disabledText
=
tester
.
widget
<
DefaultTextStyle
>(
find
.
descendant
(
of:
find
.
byKey
(
disabledPopupItemKey
),
matching:
find
.
byType
(
DefaultTextStyle
),
).
last
,
);
expect
(
disabledText
.
style
.
color
,
theme
.
disabledColor
);
final
Offset
topLeftButton
=
tester
.
getTopLeft
(
find
.
byType
(
PopupMenuButton
<
void
>));
final
Offset
topLeftMenu
=
tester
.
getTopLeft
(
find
.
byWidget
(
button
));
expect
(
topLeftMenu
,
topLeftButton
);
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
();
addTearDown
(
gesture
.
removePointer
);
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byKey
(
disabledPopupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
basic
,
);
await
gesture
.
down
(
tester
.
getCenter
(
find
.
byKey
(
enabledPopupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
click
,
);
});
testWidgets
(
'Popup menu uses values from PopupMenuThemeData'
,
(
WidgetTester
tester
)
async
{
final
PopupMenuThemeData
popupMenuTheme
=
_popupMenuThemeM2
();
final
Key
popupButtonKey
=
UniqueKey
();
final
Key
popupButtonApp
=
UniqueKey
();
final
Key
enabledPopupItemKey
=
UniqueKey
();
final
Key
disabledPopupItemKey
=
UniqueKey
();
await
tester
.
pumpWidget
(
MaterialApp
(
theme:
ThemeData
(
popupMenuTheme:
popupMenuTheme
),
key:
popupButtonApp
,
home:
Material
(
child:
Column
(
children:
<
Widget
>[
PopupMenuButton
<
void
>(
// The padding is used in the positioning of the menu when the
// position is `PopupMenuPosition.under`. Setting it to zero makes
// it easier to test.
padding:
EdgeInsets
.
zero
,
key:
popupButtonKey
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuEntry
<
void
>>[
PopupMenuItem
<
void
>(
return
<
PopupMenuEntry
<
Object
>>[
PopupMenuItem
<
Object
>(
key:
disabledPopupItemKey
,
enabled:
false
,
child:
const
Text
(
'disabled'
),
),
PopupMenuItem
<
void
>(
const
PopupMenuDivider
(),
PopupMenuItem
<
Object
>(
key:
enabledPopupItemKey
,
onTap:
()
{
},
child:
const
Text
(
'enabled'
),
...
...
@@ -325,44 +531,142 @@ void main() {
];
},
),
)
,
]
,
]
,
)
,
),
),
));
await
tester
.
tap
(
find
.
byKey
(
popupButtonKey
));
await
tester
.
pumpAndSettle
();
/// The last Material widget under popupButtonApp is the [PopupMenuButton]
/// specified above, so by finding the last descendent of popupButtonApp
/// that is of type Material, this code retrieves the built
/// [PopupMenuButton].
final
Material
button
=
tester
.
widget
<
Material
>(
find
.
descendant
(
of:
find
.
byKey
(
popupButtonApp
),
matching:
find
.
byType
(
Material
),
).
last
,
);
expect
(
button
.
color
,
Colors
.
pink
);
expect
(
button
.
shape
,
const
BeveledRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
10
))));
expect
(
button
.
elevation
,
6.0
);
final
DefaultTextStyle
text
=
tester
.
widget
<
DefaultTextStyle
>(
find
.
descendant
(
of:
find
.
byKey
(
enabledPopupItemKey
),
matching:
find
.
byType
(
DefaultTextStyle
),
),
);
expect
(
text
.
style
.
color
,
const
Color
(
0xfffff000
));
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
();
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byKey
(
disabledPopupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
contextMenu
);
await
gesture
.
down
(
tester
.
getCenter
(
find
.
byKey
(
enabledPopupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
SystemMouseCursors
.
alias
);
));
await
tester
.
tap
(
find
.
byKey
(
popupButtonKey
));
await
tester
.
pumpAndSettle
();
/// The last Material widget under popupButtonApp is the [PopupMenuButton]
/// specified above, so by finding the last descendent of popupButtonApp
/// that is of type Material, this code retrieves the built
/// [PopupMenuButton].
final
Material
button
=
tester
.
widget
<
Material
>(
find
.
descendant
(
of:
find
.
byKey
(
popupButtonApp
),
matching:
find
.
byType
(
Material
),
).
last
,
);
expect
(
button
.
color
,
popupMenuTheme
.
color
);
expect
(
button
.
shape
,
popupMenuTheme
.
shape
);
expect
(
button
.
elevation
,
popupMenuTheme
.
elevation
);
/// The last DefaultTextStyle widget under popupItemKey is the
/// [PopupMenuItem] specified above, so by finding the last descendent of
/// popupItemKey that is of type DefaultTextStyle, this code retrieves the
/// built [PopupMenuItem].
final
DefaultTextStyle
text
=
tester
.
widget
<
DefaultTextStyle
>(
find
.
descendant
(
of:
find
.
byKey
(
enabledPopupItemKey
),
matching:
find
.
byType
(
DefaultTextStyle
),
).
last
,
);
expect
(
text
.
style
,
popupMenuTheme
.
textStyle
);
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
();
addTearDown
(
gesture
.
removePointer
);
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byKey
(
disabledPopupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
popupMenuTheme
.
mouseCursor
?.
resolve
(
disabled
),
);
await
gesture
.
down
(
tester
.
getCenter
(
find
.
byKey
(
enabledPopupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
popupMenuTheme
.
mouseCursor
?.
resolve
(
enabled
),
);
});
testWidgets
(
'Popup menu widget properties take priority over theme'
,
(
WidgetTester
tester
)
async
{
final
PopupMenuThemeData
popupMenuTheme
=
_popupMenuThemeM2
();
final
Key
popupButtonKey
=
UniqueKey
();
final
Key
popupButtonApp
=
UniqueKey
();
final
Key
popupItemKey
=
UniqueKey
();
const
Color
color
=
Colors
.
purple
;
const
Color
surfaceTintColor
=
Colors
.
amber
;
const
Color
shadowColor
=
Colors
.
green
;
const
ShapeBorder
shape
=
RoundedRectangleBorder
(
borderRadius:
BorderRadius
.
all
(
Radius
.
circular
(
9.0
)),
);
const
double
elevation
=
7.0
;
const
TextStyle
textStyle
=
TextStyle
(
color:
Color
(
0xffffffef
),
fontSize:
19.0
);
const
MouseCursor
cursor
=
SystemMouseCursors
.
forbidden
;
await
tester
.
pumpWidget
(
MaterialApp
(
theme:
ThemeData
(
useMaterial3:
true
,
popupMenuTheme:
popupMenuTheme
),
key:
popupButtonApp
,
home:
Material
(
child:
Column
(
children:
<
Widget
>[
PopupMenuButton
<
void
>(
key:
popupButtonKey
,
elevation:
elevation
,
shadowColor:
shadowColor
,
surfaceTintColor:
surfaceTintColor
,
color:
color
,
shape:
shape
,
itemBuilder:
(
BuildContext
context
)
{
return
<
PopupMenuEntry
<
void
>>[
PopupMenuItem
<
void
>(
key:
popupItemKey
,
labelTextStyle:
MaterialStateProperty
.
all
<
TextStyle
>(
textStyle
),
mouseCursor:
cursor
,
child:
const
Text
(
'Example'
),
),
];
},
),
],
),
),
));
await
tester
.
tap
(
find
.
byKey
(
popupButtonKey
));
await
tester
.
pumpAndSettle
();
/// The last Material widget under popupButtonApp is the [PopupMenuButton]
/// specified above, so by finding the last descendent of popupButtonApp
/// that is of type Material, this code retrieves the built
/// [PopupMenuButton].
final
Material
button
=
tester
.
widget
<
Material
>(
find
.
descendant
(
of:
find
.
byKey
(
popupButtonApp
),
matching:
find
.
byType
(
Material
),
).
last
,
);
expect
(
button
.
color
,
color
);
expect
(
button
.
shape
,
shape
);
expect
(
button
.
elevation
,
elevation
);
expect
(
button
.
shadowColor
,
shadowColor
);
expect
(
button
.
surfaceTintColor
,
surfaceTintColor
);
/// The last DefaultTextStyle widget under popupItemKey is the
/// [PopupMenuItem] specified above, so by finding the last descendent of
/// popupItemKey that is of type DefaultTextStyle, this code retrieves the
/// built [PopupMenuItem].
final
DefaultTextStyle
text
=
tester
.
widget
<
DefaultTextStyle
>(
find
.
descendant
(
of:
find
.
byKey
(
popupItemKey
),
matching:
find
.
byType
(
DefaultTextStyle
),
).
last
,
);
expect
(
text
.
style
,
textStyle
);
final
TestGesture
gesture
=
await
tester
.
createGesture
(
kind:
PointerDeviceKind
.
mouse
);
await
gesture
.
addPointer
();
addTearDown
(
gesture
.
removePointer
);
await
gesture
.
moveTo
(
tester
.
getCenter
(
find
.
byKey
(
popupItemKey
)));
await
tester
.
pumpAndSettle
();
expect
(
RendererBinding
.
instance
.
mouseTracker
.
debugDeviceActiveCursor
(
1
),
cursor
);
});
});
}
Set
<
MaterialState
>
enabled
=
<
MaterialState
>{};
Set
<
MaterialState
>
disabled
=
<
MaterialState
>{
MaterialState
.
disabled
};
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