Skip to content

Instantly share code, notes, and snippets.

@diegoveloper
Created April 20, 2019 04:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save diegoveloper/a388dd42a01ffff04cd51ec026381fe3 to your computer and use it in GitHub Desktop.
Save diegoveloper/a388dd42a01ffff04cd51ec026381fe3 to your computer and use it in GitHub Desktop.
import 'package:flutter/material.dart';
class MyOwnPopupMenuButton<T> extends StatefulWidget {
/// Creates a button that shows a popup menu.
///
/// The [itemBuilder] argument must not be null.
const MyOwnPopupMenuButton({
Key key,
@required this.itemBuilder,
this.initialValue,
this.onSelected,
this.onCanceled,
this.tooltip,
this.elevation = 8.0,
this.enabled = true,
this.padding = const EdgeInsets.all(8.0),
this.child,
this.icon,
this.offset = Offset.zero,
}) : assert(itemBuilder != null),
assert(offset != null),
assert(!(child != null &&
icon != null)), // fails if passed both parameters
super(key: key);
/// Called when the button is pressed to create the items to show in the menu.
final PopupMenuItemBuilder<T> itemBuilder;
/// The value of the menu item, if any, that should be highlighted when the menu opens.
final T initialValue;
/// Called when the user selects a value from the popup menu created by this button.
///
/// If the popup menu is dismissed without selecting a value, [onCanceled] is
/// called instead.
final PopupMenuItemSelected<T> onSelected;
/// Called when the user dismisses the popup menu without selecting an item.
///
/// If the user selects a value, [onSelected] is called instead.
final PopupMenuCanceled onCanceled;
/// Text that describes the action that will occur when the button is pressed.
///
/// This text is displayed when the user long-presses on the button and is
/// used for accessibility.
final String tooltip;
/// The z-coordinate at which to place the menu when open. This controls the
/// size of the shadow below the menu.
///
/// Defaults to 8, the appropriate elevation for popup menus.
final double elevation;
/// 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.
final EdgeInsetsGeometry padding;
/// If provided, the widget used for this button.
final Widget child;
/// If provided, the icon used for this button.
final Icon icon;
/// The offset applied to the Popup Menu Button.
///
/// When not set, the Popup Menu Button will be positioned directly next to
/// the button that was used to create it.
final Offset offset;
final bool enabled;
@override
_PopupMenuButtonState<T> createState() => _PopupMenuButtonState<T>();
}
class _PopupMenuButtonState<T> extends State<MyOwnPopupMenuButton<T>> {
void showButtonMenu() {
final RenderBox button = context.findRenderObject();
final RenderBox overlay = Overlay.of(context).context.findRenderObject();
final RelativeRect position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(widget.offset, ancestor: overlay),
button.localToGlobal(button.size.bottomRight(Offset.zero),
ancestor: overlay),
),
Offset.zero & overlay.size,
);
showMenu<T>(
context: context,
elevation: widget.elevation,
items: widget.itemBuilder(context),
initialValue: widget.initialValue,
position: position,
).then<void>((T newValue) {
if (!mounted) return null;
if (newValue == null) {
if (widget.onCanceled != null) widget.onCanceled();
return null;
}
if (widget.onSelected != null) widget.onSelected(newValue);
});
}
Icon _getIcon(TargetPlatform platform) {
assert(platform != null);
switch (platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return const Icon(Icons.more_vert);
case TargetPlatform.iOS:
return const Icon(Icons.more_horiz);
}
return null;
}
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterialLocalizations(context));
return widget.child != null
? InkWell(
onTap: widget.enabled ? showButtonMenu : null,
child: widget.child,
)
: IconButton(
icon: widget.icon ?? _getIcon(Theme.of(context).platform),
padding: widget.padding,
tooltip: widget.tooltip ??
MaterialLocalizations.of(context).showMenuTooltip,
onPressed: widget.enabled ? showButtonMenu : null,
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment