artoniaz
artoniaz

Reputation: 183

Flutter: popup menu inside popup menu

I need to achieve a list of PopUpMenu nested inside a PopUpMenu. On click on one of the itmes I want to get another PopUpMenu with it's own items.

We could say it maight look similar to classic windows options.

Is it possible to achievie in flutter?

Upvotes: 2

Views: 4841

Answers (3)

q1224991097
q1224991097

Reputation: 21

You can do like this, add it to code.

class PopupMenuChildrenItem<T> extends PopupMenuEntry<T> {
  const PopupMenuChildrenItem({
    super.key,
    this.height = kMinInteractiveDimension,
    this.padding,
    this.enable = true,
    this.textStyle,
    this.onTap,
    required this.itemBuilder,
    required this.child,
  });

  final TextStyle? textStyle;
  final EdgeInsets? padding;
  final bool enable;
  final void Function()? onTap;
  final List<PopupMenuEntry<T>> Function(BuildContext) itemBuilder;
  final Widget child;

  @override
  final double height;

  @override
  bool represents(T? value) => false;

  @override
  MyPopupMenuItemState<T, PopupMenuChildrenItem<T>> createState() => MyPopupMenuItemState<T, PopupMenuChildrenItem<T>>();
}

class MyPopupMenuItemState<T, W extends PopupMenuChildrenItem<T>> extends State<W> {
  @protected
  void handleTap(T value) {
    widget.onTap?.call();

    Navigator.pop<T>(context, value);
  }

  @override
  Widget build(BuildContext context) {
    final ThemeData theme = Theme.of(context);
    final PopupMenuThemeData popupMenuTheme = PopupMenuTheme.of(context);
    TextStyle style = widget.textStyle ??
        popupMenuTheme.textStyle ??
        theme.textTheme.subtitle1!;

    return PopupMenuButton<T>(
      enabled: widget.enable,
      onSelected: handleTap,
      itemBuilder: widget.itemBuilder,
      child: AnimatedDefaultTextStyle(
        style: style,
        duration: kThemeChangeDuration,
        child: Container(
          alignment: AlignmentDirectional.centerStart,
          constraints: BoxConstraints(minHeight: widget.height),
          padding: widget.padding ?? const EdgeInsets.symmetric(horizontal: 16),
          child: widget.child,
        ),
      ),
    );
  }
}

Then, use it like

PopupMenuButton<int>(
      child: const Text('MENU'),
      onSelected: (result) {
        setState(() {
          _selection = result;
        });
      },
      itemBuilder: (BuildContext context) => [
        PopupMenuChildrenItem(
          child: const Text('SUBMENU A'),
          itemBuilder: (BuildContext context) => [
            const PopupMenuItem(
              value: 1,
              child: Text('1'),
            ),
            const PopupMenuItem(
              value: 2,
              child: Text('2'),
            ),
          ],
        ),
        PopupMenuChildrenItem(
          child: const Text('SUBMENU B'),
          itemBuilder: (BuildContext context) => [
            const PopupMenuItem(
              value: 3,
              child: Text('3'),
            ),
            const PopupMenuItem(
              value: 4,
              child: Text('4'),
            ),
          ],
        ),
        const PopupMenuItem(
          value: 5,
          child: Text('5'),
        ),
        const PopupMenuItem(
          value: 6,
          child: Text('6'),
        ),
      ],
    )

Upvotes: 0

gbaccetta
gbaccetta

Reputation: 4577

Sure you can:

This show two subMenu with two items each. I used an enum for the demo: enum Item { i1, i2, i3, i4 }

Make sure to call Navigator.pop(context) in the onSelected to close the first menu.

PopupMenuButton(
              
              child: Text('MENU'),
              itemBuilder: (BuildContext context) => <PopupMenuEntry<PopupMenuButton>>[
                PopupMenuItem(
                  child: PopupMenuButton(
                    child: Text('SUBMENU A'),
                    onSelected: (Item result) { 
                      setState(() { _selection = result; });
                    Navigator.pop(context); },
                    itemBuilder: (BuildContext context) => <PopupMenuEntry<Item>>[
                      const PopupMenuItem<Item>(
                        value: Item.i1,
                        child: Text('i1'),
                        ),
                      const PopupMenuItem<Item>(
                        value: Item.i2,
                        child: Text('i2'),
                        ),
                    ],
                  ),
                  ),
                
              PopupMenuItem(
                child: PopupMenuButton(
                    child: Text('SUBMENU B'),
                    onSelected: (Item result) { 
                      setState(() { _selection = result; });
                      Navigator.pop(context); },
                    itemBuilder: (BuildContext context) => <PopupMenuEntry<Item>>[
                      const PopupMenuItem<Item>(
                        value: Item.i3,
                        child: Text('i3'),
                        ),
                      const PopupMenuItem<Item>(
                        value: Item.i4,
                        child: Text('i4'),
                        ),
                    ],
                  ),
                ),
              ],
            ),

Upvotes: 3

Hosam Hasan
Hosam Hasan

Reputation: 692

you can get nested pop up menus like this :

PopupMenuButton(
  itemBuilder: (_) {
    return [
      PopupMenuItem(child: Text("Item1")),
      PopupMenuItem(
        child: PopupMenuButton(
          child: Text("Nested Items"),
          itemBuilder: (_) {
            return [
              PopupMenuItem(child: Text("Item2")),
              PopupMenuItem(child: Text("Item3"))
            ];
          },
        ),
      ),
    ];
  },
)

Upvotes: 2

Related Questions