Search code examples
flutterpopuppopupmenu

Event for PopupMenuButton is opened (activated) in Flutter


What is the event that is triggered from PopupMenuButton or any other approach, so that when in the example code attached, when user opens the pop-up menu from the right side of items, does sets the selectedIndex to the row number of the ListView?

Basically, when a user opens the popup menu, I want to select the row, but currently when the popup is opened, it does not even get fired that I can use to set the value. The onTap does not get executed till the row is selected.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  final GlobalKey<ScaffoldState> _scaffoldkey = new GlobalKey<ScaffoldState>();

  final List<String> _names = [
    'Liam', 'Noah', 'Oliver', 'William', 'Elijah',
  ];

  int selectedIndex = -1 ;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldkey,
      appBar: AppBar(
        title: Text('Pop Menu with List'),
      ),
      body: ListView.builder(
        itemBuilder: (_, i) {
          String name = _names[i];
          return ListTile(
              title: (selectedIndex == i)  ? Text('$name', style: TextStyle(fontWeight: FontWeight.bold),) : Text('$name'),
              onTap: () {
                setState(() {
                  selectedIndex =  i;
                });
            },
            trailing: PopupMenuButton(
              icon: Icon(Icons.more_vert),
              itemBuilder: (context) {
                return [
                  PopupMenuItem(
                    value: 'edit',
                    child: Text('Edit'),
                  ),
                  PopupMenuItem(
                    value: 'delete',
                    child: Text('Delete'),
                  )
                ];
              },
              onSelected: (String value) => actionPopUpItemSelected(value, name),
            ),
          );
        },
        itemCount: _names.length,
      ),
    );
  }

  void actionPopUpItemSelected(String value, String name) {
    // _scaffoldkey.currentState.hideCurrentSnackBar();
    ScaffoldMessenger.of(context).hideCurrentSnackBar;
    String message;
    if (value == 'edit') {
      message = 'You selected edit for $name';
    } else if (value == 'delete') {
      message = 'You selected delete for $name';
    } else {
      message = 'Not implemented';
    }
    final snackBar = SnackBar(content: Text(message));
    // _scaffoldkey.currentState.showSnackBar(snackBar);
    ScaffoldMessenger.of(context).showSnackBar(snackBar);
  }

}

The sample code is adapted from Flutter: Pop up menu list view.


Solution

  • Try using the gesture detector with the showMenu method.

    I used Inkwell and Ink here to give a more natural button feel, but it’s the same functionality.

    body: ListView.builder(
        itemBuilder: (_, i) {
          String name = _names[i];
          return ListTile(
            title: (selectedIndex == i)
                ? Text(
                    '$name',
                    style: TextStyle(fontWeight: FontWeight.bold),
                  )
                : Text('$name'),
            trailing: InkWell(
              customBorder: CircleBorder(),
              child: Ink(
                  padding: const EdgeInsets.all(2),
                  child: Icon(Icons.more_vert)),
              onTapDown: (details) async {
                setState(() {
                  selectedIndex = i;
                });
    
                final RenderBox overlay = Overlay.of(context)!
                    .context
                    .findRenderObject() as RenderBox;
    
                await showMenu(
                  context: context,
                  position: RelativeRect.fromRect(
                      details.globalPosition & const Size(40, 40),
                      Offset.zero & overlay.size),
                  items: const [
                    PopupMenuItem(
                      value: 'edit',
                      child: Text('Edit'),
                    ),
                    PopupMenuItem(
                      value: 'delete',
                      child: Text('Delete'),
                    )
                  ],
                  elevation: 8.0,
                ).then((String? v) => actionPopUpItemSelected(v!, name));
              },
            ),
          );
        },
        itemCount: _names.length,
      ),