Flutter On Long Press Show Popup Menu Items
gestureDetector long press show menu

Hi Guys, Welcome to Proto Coders Point. In this Flutter article let’s learn how to show popup menu item on long press on a widget.

There might be a requirement in flutter app development. i.e. when a user long press on a widget we should show a context menu item, & the position of flutter popup menu should be near by the long tapped location of the screen.

Video Tutorial

Flutter popup menu on long press

To get screen tapped position i.e. X & Y Coordinates will make use of a widget called as GestureDetector. You just need to wrap a widget with GestureDetector on which user can long press to get popup context menu near to the tapped position.

Snippet Code:

 GestureDetector(
                    onTapDown: (position)=>{
                      _getTapPosition(position)  /* get screen tap position */
                    },onLongPress: ()=>{
                    _showContextMenu(context)   /* action on long press 
                  },
 
                  child: Image.network('https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?cs=srgb&dl=pexels-anjana-c-674010.jpg&fm=jpg',width: 300,height: 300,)
                  )

Function – Flutter Get Tap position X & Y Coordinates

The below function will help us in getting user tap position i.e. X & Y coordinate of our mobile screen.

Snippet Code:

void _getTapPosition(TapDownDetails tapPosition){
     final RenderBox referenceBox = context.findRenderObject() as RenderBox;
     setState(() {
       _tapPosition = referenceBox.globalToLocal(tapPosition.globalPosition);   // store the tap positon in offset variable
       print(_tapPosition);
     });
  }

Flutter popup menu on long press at tap position

The below function will show a popup context menu item at long press position on the screen.

In out function, will use a in-built function i.e. showMenu that will help use in showing context menu items.

In showMenu function, we need to pass 3 parameter:

  • context:
  • position: /* position where user have long pressed to load popup menu items. /*
  • items: /* list of popupmenuItem */

Menu function snippet code

Snippet Code:

void _showContextMenu(BuildContext context) async {
    final RenderObject? overlay =
        Overlay.of(context)?.context.findRenderObject();
    
    final result = await showMenu(
        context: context,
        position: RelativeRect.fromRect(
            Rect.fromLTWH(_tapPosition.dx, _tapPosition.dy, 100, 100),
            Rect.fromLTWH(0, 0, overlay!.paintBounds.size.width,
                overlay!.paintBounds.size.height)),
        items: [
          const PopupMenuItem(
            child: Text('Add Me'),
            value: "fav",
          ),
          const PopupMenuItem(
            child: Text('Close'),
            value: "close",
          )
        ]);
    // perform action on selected menu item
    switch (result) {
      case 'fav':
        print("fav");
        break;
      case 'close':
        print('close');
        Navigator.pop(context);
        break;
    }
  }

Flutter show popup context menu near long press position

Will keep it simple. Will have a Image Widget at the center of screen, The Image Widget is been wrapped with GestureDetector therefore, When user long press on image widget we get the tap position using onTapDown() & onLongPress() will popup a context menu items which 2 options (Add to favorite & a Close menu).

flutter long press show popup menu items

Complete Source Code

main.dart

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Offset _tapPosition = Offset.zero;

  void _getTapPosition(TapDownDetails tapPosition) {
    final RenderBox referenceBox = context.findRenderObject() as RenderBox;
    setState(() {
      _tapPosition = referenceBox.globalToLocal(tapPosition.globalPosition);
      print(_tapPosition);
    });
  }

  void _showContextMenu(BuildContext context) async {
    final RenderObject? overlay =
        Overlay.of(context)?.context.findRenderObject();

    final result = await showMenu(
        context: context,
        position: RelativeRect.fromRect(
            Rect.fromLTWH(_tapPosition.dx, _tapPosition.dy, 100, 100),
            Rect.fromLTWH(0, 0, overlay!.paintBounds.size.width,
                overlay!.paintBounds.size.height)),
        items: [
          const PopupMenuItem(
            child: Text('Add Me'),
            value: "fav",
          ),
          const PopupMenuItem(
            child: Text('Close'),
            value: "close",
          )
        ]);
    // perform action on selected menu item
    switch (result) {
      case 'fav':
        print("fav");
        break;
      case 'close':
        print('close');
        Navigator.pop(context);
        break;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('ProtoCodersPoint.com'),
          centerTitle: true,
        ),
        body: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                GestureDetector(
                    onTapDown: (position) => {_getTapPosition(position)},
                    onLongPress: () => {_showContextMenu(context)},
                    onDoubleTap: () => {_showContextMenu(context)},
                    child: Image.network(
                      'https://images.pexels.com/photos/674010/pexels-photo-674010.jpeg?cs=srgb&dl=pexels-anjana-c-674010.jpg&fm=jpg',
                      width: 300,
                      height: 300,
                    ))
              ],
            ),
          ),
        ));
  }
}