Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use radio buttons inside popup menus?

Tags:

flutter

dart

I'm trying to create a popup menu that contains selectable radio buttons in order to change a view type (e.g. gallery, cards, swipe, grid, list, etc.).

The issue I'm running into is that PopupMenu has its own callbacks for selecting values, and so does Radio and RadioListTile.

Ignore RadioListTile's onChanged

Here's my first attempt. This actually works, except that the buttons are perpetually grayed out. Giving the RadioListTiles a non-null noop function results in the buttons no longer grayed out (disabled), but then the popup menu no longer works.

new PopupMenuButton<String>(
    ...
    itemBuilder: (ctx) => <PopupMenuEntry<String>>[
          new PopupMenuItem(
              child: new RadioListTile(
                  title: new Text("Cards"),
                  value: 'cards',
                  groupValue: _view,
                  onChanged: null),
              value: 'cards'),
          new PopupMenuItem(
              child: new RadioListTile(
                  title: new Text("Swipe"),
                  value: 'swipe',
                  groupValue: _view,
                  onChanged: null),
              value: 'swipe'),
        ],
    onSelected: (String viewType) {
      _view = viewType;
    }));

Use RadioListTile, ignore PopupMenu

Second attempt is to ignore the PopupMenu entirely and just use RadioListTile onChanged. The buttons are not grayed-out/disabled, but are also not functional.

new PopupMenuButton<String>(
  ...
  itemBuilder: (ctx) => <PopupMenuEntry<Null>>[
        new PopupMenuItem(
            child: new RadioListTile(
                title: new Text("Cards"),
                value: 'cards',
                groupValue: _view,
                onChanged: (v) => setState(() => _view = v)),
            value: 'cards'),
        new PopupMenuItem(
            child: new RadioListTile(
                title: new Text("Swipe"),
                value: 'swipe',
                groupValue: _view,
                onChanged: (v) => setState(() => _view = v)),
            value: 'swipe'),
      ],
));

What's the correct approach? PopupMenu works well with extremely simple menus, but the element selection is giving me conflicts. Is there a way to get a "dumb" popup menu that displays a column of widgets (styled like a menu) at the button?

like image 202
perlatus Avatar asked May 20 '17 11:05

perlatus


2 Answers

I think the best solution for you would be to use CheckedPopupMenuItems instead of a radio list. The functionality should be exactly what you want to achieve, isn't it?

Here is a small example:

import 'package:flutter/widgets.dart';
import 'package:flutter/material.dart';

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

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

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String _selectedView = 'Card';

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text('TestProject'),
        actions: <Widget>[
          new PopupMenuButton(
            onSelected: (value) => setState(() => _selectedView = value),
            itemBuilder: (_) => [
                  new CheckedPopupMenuItem(
                    checked: _selectedView == 'Card',
                    value: 'Card',
                    child: new Text('Card'),
                  ),
                  new CheckedPopupMenuItem(
                    checked: _selectedView == 'Swipe',
                    value: 'Swipe',
                    child: new Text('Swipe'),
                  ),
                  new CheckedPopupMenuItem(
                    checked: _selectedView == 'List',
                    value: 'List',
                    child: new Text('List'),
                  ),
                ],
          ),
        ],
      ),
      body: new Center(child: new Text(_selectedView)),
    );
  }
}
like image 149
Rainer Wittmann Avatar answered Oct 01 '22 04:10

Rainer Wittmann


The problem is that the PopupMenuButton is maintaining the popup dialog as private state (it even pushes a new route onto the Navigator stack). Calling setState won't rebuild the items. You can use an AnimatedBuilder and a ValueNotifier to get around this.

Here's an example of a working radio button list inside a popup:

screenshot

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

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

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

class MyHomePage extends StatefulWidget {
  State createState() => new MyHomePageState();
}

enum Fruit {
  apple,
  banana,
}

class MyHomePageState extends State<MyHomePage> {
  ValueNotifier<Fruit> _selectedItem = new ValueNotifier<Fruit>(Fruit.apple);

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(
        child: new PopupMenuButton<Fruit>(
          itemBuilder: (BuildContext context) {
            return new List<PopupMenuEntry<Fruit>>.generate(
              Fruit.values.length,
              (int index) {
                return new PopupMenuItem(
                  value: Fruit.values[index],
                  child: new AnimatedBuilder(
                    child: new Text(Fruit.values[index].toString()),
                    animation: _selectedItem,
                    builder: (BuildContext context, Widget child) {
                      return new RadioListTile<Fruit>(
                        value: Fruit.values[index],
                        groupValue: _selectedItem.value,
                        title: child,
                        onChanged: (Fruit value) {
                          _selectedItem.value = value;
                        },
                      );
                    },
                  ),
                );
              },
            );
          },
        ),
      ),
    );
  }
}
like image 21
Collin Jackson Avatar answered Oct 01 '22 05:10

Collin Jackson