Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Update Popup Menu to support Material 3 #103606

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions dev/tools/gen_defaults/bin/gen_defaults.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
Expand Down
46 changes: 46 additions & 0 deletions dev/tools/gen_defaults/lib/popup_menu_template.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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")};
}''';
}
126 changes: 119 additions & 7 deletions packages/flutter/lib/src/material/popup_menu.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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!),
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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
Loading