Skip to content

Instantly share code, notes, and snippets.

@loonix
Created September 23, 2021 13:30
Show Gist options
  • Save loonix/c94e6d41e0a519109c6d95005064f0a4 to your computer and use it in GitHub Desktop.
Save loonix/c94e6d41e0a519109c6d95005064f0a4 to your computer and use it in GitHub Desktop.
[Flutter] Long Press menu widget
var _tapPosition;
void _showCustomMenu() {
var overlay = Overlay.of(context)!.context.findRenderObject() as RenderBox;
showMenu(
context: context,
items: <PopupMenuEntry<int>>[PlusMinusEntry()],
position: RelativeRect.fromRect(
_tapPosition & const Size(40, 40), // smaller rect, the touch area
Offset.zero & overlay.size // Bigger rect, the entire screen
));
}
void _storePosition(TapDownDetails details) {
_tapPosition = details.globalPosition;
}
return GestureDetector(
// This does not give the tap position ...
onLongPress: _showCustomMenu,
// Have to remember it on tap-down.
onTapDown: _storePosition,
child: (....)
);
import 'package:flutter/material.dart' hide showMenu;
import 'package:flutter/material.dart' as material show showMenu;
/// A mixin to provide convenience methods to record a tap position and show a popup menu.
mixin CustomPopupMenu<T extends StatefulWidget> on State<T> {
late Offset _tapPosition;
/// Pass this method to an onTapDown parameter to record the tap position.
void storePosition(TapDownDetails details) => _tapPosition = details.globalPosition;
/// Use this method to show the menu.
// ignore: avoid_shadowing_type_parameters
Future<T?>? showMenu<T>({
required BuildContext context,
required List<PopupMenuEntry<T>> items,
required T initialValue,
double? elevation,
String? semanticLabel,
ShapeBorder? shape,
Color? color,
bool captureInheritedThemes = true,
bool useRootNavigator = false,
}) {
var overlay = Overlay.of(context)!.context.findRenderObject() as RenderBox;
return material.showMenu<T>(
context: context,
position: RelativeRect.fromLTRB(
_tapPosition.dx,
_tapPosition.dy,
overlay.size.width - _tapPosition.dx,
overlay.size.height - _tapPosition.dy,
),
items: items,
initialValue: initialValue,
elevation: elevation,
semanticLabel: semanticLabel,
shape: shape,
color: color,
useRootNavigator: useRootNavigator,
);
}
}
class PlusMinusEntry extends PopupMenuEntry<int> {
@override
double height = 100;
// height doesn't matter, as long as we are not giving
// initialValue to showMenu().
@override
PlusMinusEntryState createState() => PlusMinusEntryState();
@override
bool represents(int? n) {
return n == 1 || n == -1;
}
}
class PlusMinusEntryState extends State<PlusMinusEntry> {
void _plus1() {
// This is how you close the popup menu and return user selection.
Navigator.pop<int>(context, 1);
}
void _minus1() {
Navigator.pop<int>(context, -1);
}
@override
Widget build(BuildContext context) {
return Row(
children: <Widget>[
Expanded(child: TextButton(onPressed: _plus1, child: Text('+1'))),
Expanded(child: TextButton(onPressed: _minus1, child: Text('-1'))),
],
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment