DEV Community

Cover image for Better Flutter Menu
Greg Perry
Greg Perry

Posted on • Edited on

Better Flutter Menu

Flutter’s popup menu needed improvement

I never liked Flutter’s popup menu. It’s ugly. It’s a block of white in the corner of the screen. Does it have to be just a square like that slapped on top of the AppBar? It doesn’t look good. It looks amateurish. Why can’t it look more like the menu below on the right-hand side? That’s a little better.

The Clutter of Flutter

Another thing I don’t like about Flutter’s popup menus is the clutter. I hate the ‘Flutter clutter!’ You know what I mean. I suspect, that when you were first learning Flutter, you too had to get used to those long vertical lists of parameters. Many widgets have long lists of parameters, and the widget, PopupMenuBotton, is no exception.

It lists its menu items, of course, but it also lists the settings on how the menu should appear — assigning those settings, by the way, you’ll get the ‘better appearance’ as seen in the screenshot above — at least that’s readily available. However, it also can list its anonymous functions making the clutter even longer. I mean, you don’t even see the popup menu until you tap on those three little dots in the corner of the screen, and yet its code can take up half the build() function. See below. No sir! I don’t like it.

So, you know what I did? Guess what I did. I wrote a class library and made the popup menu better for overall app development. That’s what I did. Wanna take a look? Let’s take a look.

I Like Screenshots. Tap Caption For Gists.

As always, I prefer using screenshots in my articles over gists to show concepts rather than just show code. I find them easier to work with frankly. However, you can click or tap on their captions to see the code in a gist or in Github. Tap or click on the screenshots themselves to zoom in on them for a closer look. If reading on your phone, you’ll have to then use your two fingers to expand and collapse the image. Ironically, you may find it best to read this article about mobile development on your computer rather than on your phone.

No Moving Pictures, No Social Media

There will be *gif *files in this article demonstrating aspects of the topic at hand. However, it’s said viewing such *gif *files is not possible when reading this article on platforms like Instagram or Facebook. They may come out as static pictures or simply blank placeholders. Please, be aware of this.

Let’s begin.

Other stories by Greg Perry

Here is the link to the example app, which we’ll be using for this article. In the first screenshot below, you see its AppBar widget lists more than one PopupMenuButton widget in the parameter, actions. If you tap the image’s caption and look at the code on GitHub, you’ll see there are five PopupMenuButton widgets listed with the AppBar!

There are five of them, and yet they’re not making up half the code in the State class. There’s another improvement for you right there! Some have been assigned to a particular property field or variable cleaning up the ‘actions’ parameter a little bit. However, that’s not the sole reason to assign such a widget to a memory variable. You’ll see soon enough.

For now, it’s less ‘Flutter clutter’ to distract the developer from what’s to be displayed from the State object. Further, the ‘menu’ code is a little more modular. In other words, for the first three popup menus anyway (appMenu01, appMenu02, and appMenu03), the PopupMenuButton code resides elsewhere. Two are separate classes and one is in a getter. Again, such arrangements have their advantages, and you’ll see this soon.

main.dart

It’s A Wrap

Let’s first take a closer look at the class library I wrote. Upon further inspection, you’ll see I simply wrote a wrapper class for Flutter’s PopupMenuButton widget. In the screenshot below, you see this class extends from a StatelessWidget (by the way, Flutter’s PopupMenuButton is a StatefulWidget). The wrapper’s parameters mirror the parameters found in the PopupMenuButton widget itself. As a wrapper class, it eventually instantiates a PopupMenuButton object passing in those parameter values. Follow me so far? It’s pretty straightforward.

app_popup_menu.dart

So what’s the need for a wrapper class? Let’s continue examing the code and find out. After the formal parameter, position, things begin to diverge making what I feel is a more useful PopupMenu class for developing apps. You’ll see the names of the formal parameters now begin with ‘in.’ These are the ‘in-line’ functions that you can now use instead of the traditional parameters first listed above.

You instead supply these functions to the PopupMenuButton widget. Why functions? There will be times when a more elaborate algorithm is required to determine the shape or color of your popup menu for example. You’ll be surprised how often such instances come up, and now you’ll have the means to easily address them with the appropriate function. Believe me, it’ll come up. It’s a great help.

app_popup_menu.dart

There’s A Function For That

However, how these ‘in line’ functions are implemented gives the developer even further options. Let’s continue and examine the screenshot below. It displays the portion of the PopupMenu wrapper class where the PopupMenuButton is being instantiated with its many parameters supplied possible values by the wrapper class.

You can readily see the wrapper’s formal parameters being passed to the PopupMenuButton widget. If they’re null, the ‘if null’ operator (??) then tries for a value by calling these ‘on’ functions you’ve yet to see. Where are the ‘in line’ functions we were just talking about??

app_popup_menu.dart

app_popup_menu.dart

The ‘in’ functions are in their corresponding ‘on’ functions! Now, what does that tell you? Remember, the in-line functions that begin with ‘in’ allows the PopupMenuButton to receive running functions as parameters along with the traditional formal parameters. However, if you decide to extend the wrapper class with your own subclass, you’re then free to override the many ‘on’ functions and further customize your popup menu. Making for a more modular, high cohesion, low coupling approach when supplying an app menu to your app — a cornerstone of good software development.

See what I mean? Remember those first three popup menus (appMenu01, appMenu02, and appMenu03)? Both appMenu01 and appMenu03 are in separate classes. See below. Each extends the class, AppPopupMenu, which uses PopupMenu. The class, AppPopupMenu, accompanies the class, PopupMenu, in the same Dart file and allows the developer to change the many parameter values ‘on the fly’ while running the app — those parameters are final in PopupMenu class but are not in the AppPopupMenu class. You’ll see more on this shortly.

subclass01

subclass03

First, let’s review. As you see in the screenshots of the wrapper class below, if the ‘on’ functions are not overridden in a subclass, they are still called but only their corresponding ‘in’ functions if any are actually executed. See how that works with the conditional expressions (?:) in each ‘on’ function? If no ‘in-line’ function is supplied, a null is returned and instead supplied to the PopupMenuButton widget.

app_popup_menu.dart

app_popup_menu.dart

As any wrapper class should, it does all this work for you so you don’t have to. Just implement your app’s popup menu in the fashion you want, and you’re good to go. Follow me so far?

Some Items Take Precedence

By the way, did you note the onItemBuilder() function supplied to the PopupMenuButton’s ‘required’ parameter, itemBuilder? I’ve displayed that screenshot again below. It determines the menu items that will appear in the popup menu — even when there’s more than one list made available. Why is there more than one possible list of menu items? Because I like options. If you’ve read my past articles, you know I like options.

app_popup_menu.dart

Reading the function’s code below from top to bottom will give you its order of precedence. In other words, anything listed in the formal parameter, items, will take precedence over anything assigned to the getter, menuItems. That’s just the way it is right now. You see, I love the developer to have options. The developer can supply more than one listing involving menu items, but only one source will be displayed for now. Maybe, in time, I’ll have it all, no matter how it’s provided, listed in a specific order. However, the need just hasn’t come up yet. Anyway, the comment lines in the code below should give you an idea of the different lists available to the developer.

app_popup_menu.dart

Frankly, others may argue the order should be reversed. That is, the items and onItems entries should have instead the lowest precedence and the itemBuilder entry the highest. If that’s how you feel, take a copy and change it! The World’s your oyster! I’ve got my copy. I just thought I’d share my approach and give you some ideas for your next app’s menu bar. Cool?

A Material Mix

The last bit of the wrapper class has the popup menu object wrapped in a Material widget if, for example, you’re running an AppBar in a Cupertino interface. You wrap it so it won’t crash. A simple little trick. Again, the wrapper class worries about such things so you don’t have to. What follows the class in the screenshot below is a mixin named, PopupMenuButtonFunctions, it’s used by the AppPopupMenu class to supply the many ‘on’ functions defined in this class library. Repeated code is a big no-no in good software development practices — mixins are a useful tool in that regard. Remember to tap on the image captions to see for yourself on GitHub.

app_popup_menu.dart

A More Functional Approach

Again, the class, AppPopMenu, allows you to change the many parameter values ‘on the fly’ while running the app. It can do this because, unlike the PopupMenu class, its many formal parameters you see below are not final field properties — they can be reassigned again and again as the app is running. Properties can be reassigned right from the instance object: appMenu03.elevation = 14;

app_popup_menu.dart

app_popup_menu.dart

As you see below, its getter, popupMenuButton, calls a private function called _popupMenuButton(). That function returns a Widget that contains the wrapper class, PopupMenu. And because the class, AppPopupMenu, is explicitly passed to this function, you have a popup menu available to you that you can dynamically change at runtime: _popMenuButton(this);

app_popup_menu.dart

app_popup_menu.dart

With Great Power…

Now such capability should be used carefully. Anyone with access to that instance variable can manipulate an app’s popup menu —someone who shouldn’t. Granted, the need to change the content and behavior of your app’s popup menu shouldn’t come up too often. I mean, an app’s menu traditionally remains static and unchanging. However, if the need arises, the AppPopupMenu class will prove to be a quick solution. Otherwise, the PopupMenu class should instead be your ‘go-to’ implementation.

Get A Menu

Always remember to tap on this article’s images to get a closer look. Anyway, let’s return to the example app and talk about my favorite popup menu implementation: Using a getter. With a getter, the wrapper class, PopupMenu, is not accessed until the getter is — an important fact if you don’t want it evaluated when your app is starting up. Some of the elements that make up your popup menu may not ‘be ready’ at start-up. They’ll be ready only later at runtime. A getter is a great vehicle for such cases.

For demonstration purposes, I’ve implemented all the ‘in’ functions available to you, but of course, one should just implement those needed to get the desired ‘look and feel’ — its desired appearance and behavior. It’s a clean and efficient approach. I like it.

main.dart

main.dart

main.dart

main.dart

A Simple Menu

The fourth popup menu displayed in this example app (see below) has the ‘plus sign in a circle’ icon. In this one, we’re simply instantiating the PopupMenu class right in the AppBar’s actions parameter. As you see highlighted below, when a menu option is selected or not, the two parameters, onSelected and onCanceled, are assigned anonymous functions involving a SnackBar object. The remaining formal parameters are assigned values concerned with the general appearance of the popup menu when opened. Not my favorite approach with its clutter, but certainly another option for developers.

main.dart

main.dart

In First Class

Next, highlighted below is the class, SubClass01, and it’s the first popup menu listed in the AppBar’s actions parameter. As you now know, it’s a subclass of the AppPopupMenu class, which calls the PopupMenu class. In the screenshot below, the only parameter value passed when instantiated is a key identifier. True, because it extends the class, AppPopupMenu, you’re free to assign additional values to this instance anytime, anywhere while your app is running. However, you’ve also access to its BuildContext object.

main.dart

Looking at the class itself, we see it’s really light in content. You can see below only the menu items list, and the two ‘select items or not’ functions are defined in the class. Note, the context object referenced in those two functions. It’s required to utilize the SnackBar widget.

subclass01.dart

subclass01.dart

Keep It in Context

The only other reason, I would think you’d use the AppPopupMenu class is because it has access to a BuildContext object in the form of a getter (see below) allowing your popup menu to open screens and such in your app’s interface.

app_popup_menu.dart

Back to our example app, you can see the instance variable, appMenu01, representing the SubClass01 class gives us that ugly ‘white block’ of options in the gif file displayed below. It simply hasn’t much set in its class definition. Of course, you’re free to change it as it’s an AppPopuMenu.

main.dart

Unlike the SubClass01 class, the class, SubClass05, directly extends from the wrapper class, PopupMenu. The screenshots below depict what’s implemented and what’s overridden. You’ll see many overridden are merely returning null. They’re overridden for demonstration purposes and need not be there at all — just to show you the many ‘on’ functions available to you.

subclass05.dart

subclass05.dart

subclass05.dart

subclass05.dart

subclass05.dart

subclass05.dart

subclass05.dart

A Dynamic Menu

The last menu example also extends the AppPopupMenu class. It’s instantiated in the screenshot below, and you can tell it extends the AppPopupMenu class because its getter, context, is accessed in its ‘onSelected’ anonymous function.

main.dart

The next two screenshots show you the instance variable mutable properties being assigned values. As it happens, this is all being done in an initState() function. Conceivably, depending on the variable’s scope, you can change these at the appropriate time, place, and circumstance.

main.dart

main.dart

main.dart

In many cases, a popup menu provides much of an app’s functionality. Initially, out of sight, a user taps on an app bar to reveal a popup menu — a popup menu that’s possibly full of options. Once an option is selected, it’s out of sight again. A very effective interface. Now you have a class library to supply this popup menu — hopefully, just as effective.

Cheers.

Greg Perry

Top comments (0)