1.什么是上下文菜单
上下文菜单主要是指一种通过上下文贯穿多级组件的特定的弹窗菜单,如pc端的鼠标右击菜单移动端的长按菜单或内容选择菜单
2.在flutter中使用上下文菜单
需要使用ContextMenuController创建上下文菜单控制器实例,并使用show方法弹出上下文菜单,首先我们来看一下ContextMenuController有那些可用的参数与方法。
ContextMenuController构造函数参数
参数名 | 类型 | 必选 | 描述 |
---|---|---|---|
onRemove | VoidCallback | 否 | 当上下文菜单被移除时触发 |
ContextMenuController可使用的成员变量
变量名 | 类型 | 描述 |
---|---|---|
isShown | bool | 当前上下文菜单是否处于展示状态 |
ContextMenuController可调用的方法
方法名 | 参数 | 返回值 | 描述 |
---|---|---|---|
show | { required BuildContext context, required WidgetBuilder contextMenuBuilder, Widget? debugRequiredFor } |
void | 展示上下文菜单 |
remove | - | void | 从视图中删除上下文菜单 |
markNeedsBuild | - | void | 重建视图中的上下文菜单 |
从show方法的源码可以看出每次展示上下菜单时都会销毁已展示的上下文菜单,并将新创建的OverlayEntry实例存放在静态变量_menuOverlayEntry上,上下文菜单的本质是使用Overlay+OverlayEntry实现的浮层,完全可以参考ContextMenuController实现自己的上下文菜单组件。
2.1默认上下文菜单
展示上下使用show方法即可,官方提供了AdaptiveTextSelectionToolbar.buttonItems方法创建简单的上下文菜单,需要注意上下文菜单需要使用anchors参数指定菜单展示位置。
class NoteList extends StatefulWidget {
const NoteList({super.key});
@override
State<NoteList> createState() => _NoteListState();
}
class _NoteListState extends State<NoteList> {
/// 上下文菜单控制器
final ContextMenuController _contextMenuController = ContextMenuController();
Widget _buildListItem(){
return GestureDetector(
onSecondaryTapUp: (details) {
_showContextMenu(details.globalPosition);
}
);
}
void _showContextMenu(Offset position){
/// 展示上下文菜单
_contextMenuController.show(
context: context,
contextMenuBuilder: (context) {
return AdaptiveTextSelectionToolbar.buttonItems(
/// 指定菜单展示的位置,使用鼠标右击的全局位置信息
anchors: TextSelectionToolbarAnchors(primaryAnchor: position),
buttonItems: <ContextMenuButtonItem>[
ContextMenuButtonItem(
label: '删除',
onPressed: () {}
)
]
);
}
);
}
}
在windows平台的效果
2.2.自定义上下文菜单
定位到AdaptiveTextSelectionToolbar的源码可以发现buttonItems的数据最终会根据所属平台不同而使用对应的模板渲染。
从AdaptiveTextSelectionToolbar的build代码可以看出我们只需要使用children属性就可以实现自定义上下文菜单项。
如下代码使用安卓的上下文明细行渲染
_contextMenuController.show(
context: context,
contextMenuBuilder: (context) {
return AdaptiveTextSelectionToolbar(
anchors: TextSelectionToolbarAnchors(primaryAnchor: position),
children: [
TextSelectionToolbarTextButton(
padding: TextSelectionToolbarTextButton.getPadding(0, 1),
onPressed: () {},
child: const Text("删除"),
),
TextSelectionToolbarTextButton(
padding: TextSelectionToolbarTextButton.getPadding(0, 1),
onPressed: () {},
child: const Text("复制"),
)
],
);
}
);
在windows平台的效果如下
可以看到每行的渲染已发生了变化,但是布局任然是纵向排列,而安卓平台的上下文菜单是横向排列的,那么我们可以不可以让windows平台的上下文菜单如安卓平台一样横向排列或者做出多级菜单呢?当然完全可以,我们只需要在contextMenuBuilder中返回自定义的上下文菜单渲染组件就可以了,如下在windows平台使用横向布局菜单。
_contextMenuController.show(
context: context,
contextMenuBuilder: (context) {
var anchors = TextSelectionToolbarAnchors(primaryAnchor: position);
return TextSelectionToolbar(
anchorAbove: anchors.primaryAnchor,
anchorBelow: anchors.secondaryAnchor == null
? anchors.primaryAnchor
: anchors.secondaryAnchor!,
children: [
TextSelectionToolbarTextButton(
padding: TextSelectionToolbarTextButton.getPadding(0, 1),
onPressed: () {},
child: const Text("删除"),
),
TextSelectionToolbarTextButton(
padding: TextSelectionToolbarTextButton.getPadding(0, 1),
onPressed: () {},
child: const Text("复制"),
)
],
);
}
);
在windows平台使用安卓的横向布局后上下文菜单的效果