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.
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;
}));
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?
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)),
);
}
}
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:
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;
},
);
},
),
);
},
);
},
),
),
);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With