PopupMenuButtonでカスケードメニューの作り方。
初めに
一部では、サブメニューとかネストとか書かれているがFlutterのポップアップメニューでカスケードメニューが実現できるかどうか調査していたのだけど、その実現方法が分かった。
実装方法としてはそれほど難しくはなかったのだけど、いくつかだけ注意しなければいけないことがあった。
実装方法
PopupMenuButtonの項目として追加するPopupMenuItemのchildにPopupMenuButtonを設定することで可能。
実装時の注意点は以下の様になる。
- PopupMenuItemにvalueに値を設定しない。
valueに値が設定されない場合、該当箇所のメニューを選択しても、onSelectedが呼び出されず表示されているポップアップメニューもクローズされない。 - メニューに表示される文字は、PopupMenuButtonのchildに設定する。
表示される場所は上のような感じ。
childの設定がない場合、ポップアップメニュー起動用のデフォルトボタンが表示される。 - 2のchildのテキストの大きさに注意する。
childにTextだけだとカスケードメニューで起動するためのタッチ領域が小さくなる。
TextをSizedBox等で囲んで少しでも大きくしておいたほうが良い。
上の図ではSizedBoxの幅を無限にした結果、選択できる領域が横方向に大きくなった。
縦幅も大きくした方がいいのかもしれなかったのだけど、適切な大きさが分からなかったので、値の設定はしていない。
ここらあたりもチューニングしてけばいいかもしれない。縦幅はkMinInteractiveDimensionという定数を使うのが良いらしいとのこと。
これにより以下の様にメニュー内の縦方向にも選択領域が広がるようになった。
- カスケード側のonSelectedでNavigator.of(context).pop();を実行する。
ポップアップメニューはonSelectedが呼び出されるとクローズされるのだけど、親側は当然だけど親のonSelectedで処理されていないのでクローズされない。
そこで親もクローズさせるために、上の呼び出しを入れることになる。
サンプルコード
以下のサンプルコードを実施すると、以下の様なカスケードメニューが実現できる。
import 'package:flutter/material.dart';
class CascadeMenu extends StatelessWidget {
const CascadeMenu({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Spacer test"),
actions: [
PopupMenuButton<String>(
onSelected: (value) => debugPrint(value),
itemBuilder: (_) {
return [
const PopupMenuItem<String>(
value: "data",
child: Text("data"),
),
PopupMenuItem<String>(
child: PopupMenuButton<String>(
child: SizedBox(
height: kMinInteractiveDimension,
width: double.infinity,
child: Container(
alignment: Alignment.centerLeft,
child: const Text("sub")),
),
),
itemBuilder: (_) {
return [
const PopupMenuItem<String>(
value: "subdata1",
child: Text("subdata1"),
),
const PopupMenuItem<String>(
value: "subdata2",
child: Text("subdata1"),
),
];
},
onSelected: (value) {
debugPrint(value);
Navigator.of(context).pop();
},
),
),
];
},
)
],
),
body: const Center(child: Text("Hoge")),
);
}
}
コメント