popupmenu.dart 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import 'package:flutter/material.dart';
  2. class PopupMenuItem<T> extends PopupMenuEntry<T> {
  3. /// Creates an item for a popup menu.
  4. ///
  5. /// By default, the item is [enabled].
  6. ///
  7. /// The `enabled` and `height` arguments must not be null.
  8. const PopupMenuItem({
  9. Key key,
  10. this.value,
  11. this.enabled = true,
  12. this.height = kMinInteractiveDimension,
  13. this.textStyle,
  14. @required this.child,
  15. }) : assert(enabled != null),
  16. assert(height != null),
  17. super(key: key);
  18. /// The value that will be returned by [showMenu] if this entry is selected.
  19. final T value;
  20. /// Whether the user is permitted to select this item.
  21. ///
  22. /// Defaults to true. If this is false, then the item will not react to
  23. /// touches.
  24. final bool enabled;
  25. /// The minimum height height of the menu item.
  26. ///
  27. /// Defaults to [kMinInteractiveDimension] pixels.
  28. @override
  29. final double height;
  30. /// The text style of the popup menu item.
  31. ///
  32. /// If this property is null, then [PopupMenuThemeData.textStyle] is used.
  33. /// If [PopupMenuThemeData.textStyle] is also null, then [ThemeData.textTheme.subtitle1] is used.
  34. final TextStyle textStyle;
  35. /// The widget below this widget in the tree.
  36. ///
  37. /// Typically a single-line [ListTile] (for menus with icons) or a [Text]. An
  38. /// appropriate [DefaultTextStyle] is put in scope for the child. In either
  39. /// case, the text should be short enough that it won't wrap.
  40. final Widget child;
  41. @override
  42. bool represents(T value) => value == this.value;
  43. @override
  44. PopupMenuItemState<T, PopupMenuItem<T>> createState() => PopupMenuItemState<T, PopupMenuItem<T>>();
  45. }
  46. /// The [State] for [PopupMenuItem] subclasses.
  47. ///
  48. /// By default this implements the basic styling and layout of Material Design
  49. /// popup menu items.
  50. ///
  51. /// The [buildChild] method can be overridden to adjust exactly what gets placed
  52. /// in the menu. By default it returns [PopupMenuItem.child].
  53. ///
  54. /// The [handleTap] method can be overridden to adjust exactly what happens when
  55. /// the item is tapped. By default, it uses [Navigator.pop] to return the
  56. /// [PopupMenuItem.value] from the menu route.
  57. ///
  58. /// This class takes two type arguments. The second, `W`, is the exact type of
  59. /// the [Widget] that is using this [State]. It must be a subclass of
  60. /// [PopupMenuItem]. The first, `T`, must match the type argument of that widget
  61. /// class, and is the type of values returned from this menu.
  62. class PopupMenuItemState<T, W extends PopupMenuItem<T>> extends State<W> {
  63. /// The menu item contents.
  64. ///
  65. /// Used by the [build] method.
  66. ///
  67. /// By default, this returns [PopupMenuItem.child]. Override this to put
  68. /// something else in the menu entry.
  69. @protected
  70. Widget buildChild() => widget.child;
  71. /// The handler for when the user selects the menu item.
  72. ///
  73. /// Used by the [InkWell] inserted by the [build] method.
  74. ///
  75. /// By default, uses [Navigator.pop] to return the [PopupMenuItem.value] from
  76. /// the menu route.
  77. @protected
  78. void handleTap() {
  79. Navigator.pop<T>(context, widget.value);
  80. }
  81. @override
  82. Widget build(BuildContext context) {
  83. final ThemeData theme = Theme.of(context);
  84. final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context);
  85. TextStyle style = widget.textStyle ?? popupMenuTheme.textStyle ?? theme.textTheme.subtitle1;
  86. if (!widget.enabled)
  87. style = style.copyWith(color: theme.disabledColor);
  88. Widget item = AnimatedDefaultTextStyle(
  89. style: style,
  90. duration: kThemeChangeDuration,
  91. child: Container(
  92. alignment: AlignmentDirectional.centerStart,
  93. constraints: BoxConstraints(minHeight: widget.height),
  94. padding: const EdgeInsets.symmetric(horizontal: 8.0),
  95. child: buildChild(),
  96. ),
  97. );
  98. if (!widget.enabled) {
  99. final bool isDark = theme.brightness == Brightness.dark;
  100. item = IconTheme.merge(
  101. data: IconThemeData(opacity: isDark ? 0.5 : 0.38),
  102. child: item,
  103. );
  104. }
  105. return InkWell(
  106. onTap: widget.enabled ? handleTap : null,
  107. canRequestFocus: widget.enabled,
  108. child: item,
  109. );
  110. }
  111. }