Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make a reusable PopupMenuButton in Flutter

Tags:

flutter

dart

I want to make a reusable PopupMenuButton in flutter, so i can call that widget and assign the PopupMenuItem dynamically.

Here is what i've done, but it throw a framework error. I am relatively new in flutter.

here is class that supposed to be reusable:

class PopupMenu {
  PopupMenu({@required this.title, @required this.onTap});

  final String title;
  final VoidCallback onTap;
}

class PopupmMenuButtonBuilder {
  setPopup(List<PopupMenu> popupItem) {
    return PopupMenuButton<String>(
      onSelected: (_) {
        popupItem.forEach((item) => item.onTap);
      },
      itemBuilder: (BuildContext context) {
        popupItem.forEach(
          (item) {
            return <PopupMenuItem<String>>[
              PopupMenuItem<String>(
                value: item.title,
                child: Text(
                  item.title,
                ),
              ),
            ];
          },
        );
      },
    );
  }
}

and then i call the widget like this:

child: PopupmMenuButtonBuilder().setPopup([
              PopupMenu(title: 'Item 1', onTap: () => print('item 1 selected')),
              PopupMenu(title: 'Item 2', onTap: () => print('item 2 selected')),
            ]),

It shows the 3 dot icon button, but when i tap the icon it throws this error:

I/flutter ( 8509): ══╡ EXCEPTION CAUGHT BY GESTURE ╞═══════════════════════════════════════════════════════════════════
I/flutter ( 8509): The following assertion was thrown while handling a gesture:
I/flutter ( 8509): 'package:flutter/src/material/popup_menu.dart': Failed assertion: line 723 pos 10: 'items != null &&
I/flutter ( 8509): items.isNotEmpty': is not true.
I/flutter ( 8509): Either the assertion indicates an error in the framework itself, or we should provide substantially
I/flutter ( 8509): more information in this error message to help you determine and fix the underlying cause.
I/flutter ( 8509): In either case, please report this assertion by filing a bug on GitHub:
I/flutter ( 8509):   https://github.com/flutter/flutter/issues/new?template=BUG.md
I/flutter ( 8509): When the exception was thrown, this was the stack:
I/flutter ( 8509): #2      showMenu (package:flutter/src/material/popup_menu.dart:723:10)
I/flutter ( 8509): #3      _PopupMenuButtonState.showButtonMenu (package:flutter/src/material/popup_menu.dart:898:5)
I/flutter ( 8509): #4      _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:511:14)
I/flutter ( 8509): #5      _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:566:30)
I/flutter ( 8509): #6      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:166:24)
I/flutter ( 8509): #7      TapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:240:9)
I/flutter ( 8509): #8      TapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:211:7)
I/flutter ( 8509): #9      GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:156:27)
I/flutter ( 8509): #10     _WidgetsFlutterBinding&BindingBase&GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:225:20)
I/flutter ( 8509): #11     _WidgetsFlutterBinding&BindingBase&GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:199:22)
I/flutter ( 8509): #12     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerEvent (package:flutter/src/gestures/binding.dart:156:7)
I/flutter ( 8509): #13     _WidgetsFlutterBinding&BindingBase&GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:102:7)I/flutter ( 8509): #14     _WidgetsFlutterBinding&BindingBase&GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:86:7)I/flutter ( 8509): #18     _invoke1 (dart:ui/hooks.dart:233:10)
I/flutter ( 8509): #19     _dispatchPointerDataPacket (dart:ui/hooks.dart:154:5)
I/flutter ( 8509): (elided 5 frames from class _AssertionError and package dart:async)
I/flutter ( 8509): Handler: onTap
I/flutter ( 8509): Recognizer:
I/flutter ( 8509):   TapGestureRecognizer#8968f(debugOwner: GestureDetector, state: ready, won arena, finalPosition:
I/flutter ( 8509):   Offset(339.0, 54.0), sent tap down)
I/flutter ( 8509): ════════════════════════════════════════════════════════════════════════════════════════════════════
like image 489
dennbagas Avatar asked Oct 24 '25 16:10

dennbagas


1 Answers

Your itemBuilder needs to return a List. It doesn't actually return anything - note how the return is inside the forEach so it's just returning from the lambda. In general, forEach should sometimes be avoided. Also, the PopupMenuButtonBuilder class is redundant - it could be replaced with a static or top-level function.

One other thing that's unclear is why you want to call each onTap for every select. As you currently have it it's going to call every callback!

Try this:

class PopupMenu {
  PopupMenu({@required this.title, @required this.onTap});

  final String title;
  final VoidCallback onTap;

  static PopupMenuButton<String> createPopup(List<PopupMenu> popupItems) {
    return PopupMenuButton<String>(
      onSelected: (value) {
        popupItems.firstWhere((e) => e.title == value).onTap();
      },
      itemBuilder: (context) => popupItems
          .map((item) => PopupMenuItem<String>(
                value: item.title,
                child: Text(
                  item.title,
                ),
              ))
          .toList(),
    );
  }
}
like image 193
Richard Heap Avatar answered Oct 26 '25 06:10

Richard Heap



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!