背景
因为项目需要,需要监听输入框长按出现上下文菜单粘贴(Paste)事件,Flutter
中的输入框组件TextField
并不能像Android原生中EditText
原生View一样,重写onTextContextMenuItem
回调方法一样简单的做到监听粘贴按钮的点击事件,而是需要自定义ContextMenu
来实现。
实现
1. 根据TextField
提供的EditableTextState
创建自定义上下文菜单
dart
class _CustomContextMenu extends StatelessWidget {
final EditableTextState editableTextState;
final VoidCallback onPaste;
const _CustomContextMenu({required this.editableTextState, required this.onPaste});
@override
Widget build(BuildContext context) {
final List<ContextMenuButtonItem> items = editableTextState.contextMenuButtonItems;
// 创建新的菜单列表
List<ContextMenuButtonItem> customItems = [];
for (var item in items) {
print('item: ${item}');
if (item.type == ContextMenuButtonType.paste) {
customItems.add(
ContextMenuButtonItem(
onPressed: () {
if (item.onPressed != null) {
item.onPressed!();
}
onPaste();
},
label: item.type.name,
type: item.type,
),
);
} else {
if (item.label?.isNotEmpty == true || item.type.name.isNotEmpty) {
customItems.add(item);
}
}
}
return AdaptiveTextSelectionToolbar(
anchors: editableTextState.contextMenuAnchors,
children: customItems.map((item) {
return TextButton(
onPressed: item.onPressed,
child: Text(item.label ?? item.type.name, style: TextStyle(color: Colors.black)),
);
}).toList(),
);
}
}
这其中最关键 的代码在判断ContextMenuButtonItem
的类型,然后重新创建一个带有业务逻辑的全新的ContextMenuButtonItem
。其他不需要自定义的ContextMenuButtonItem
保持不变。
2. 将自定义_CustomContextMenu应用到TextField中
dart
class _PasteListenerDemoState extends State<PasteListenerDemo> {
final TextEditingController _controller = TextEditingController();
final FocusNode _focusNode = FocusNode();
final ValueNotifier<bool> _pasteClicked = ValueNotifier(false);
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(20),
child: Column(
children: [
TextField(
controller: _controller,
focusNode: _focusNode,
decoration: InputDecoration(border: OutlineInputBorder(), labelText: "尝试长按粘贴"),
contextMenuBuilder: (context, editableTextState) {
return _CustomContextMenu(
editableTextState: editableTextState,
onPaste: () {
_pasteClicked.value = true;
Future.delayed(Duration(seconds: 2), () {
_pasteClicked.value = false;
});
},
);
},
),
SizedBox(height: 20),
ValueListenableBuilder<bool>(
valueListenable: _pasteClicked,
builder: (context, value, child) {
return Text(
value ? "粘贴按钮被点击了!" : "等待粘贴操作...",
style: TextStyle(fontSize: 18, color: value ? Colors.green : Colors.grey),
);
},
),
],
),
);
}
}