【Flutter】TextField 监听长按菜单粘贴点击事件

背景

因为项目需要,需要监听输入框长按出现上下文菜单粘贴(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),
              );
            },
          ),
        ],
      ),
    );
  }
}
相关推荐
漏刻有时18 小时前
PHP GeoJSON转PNG地图渲染程序开发笔记、源码解读、问题复盘与整改方案
android·笔记·php
-SOLO-19 小时前
解决VMware 显示比例被重置的问题
android
alexhilton20 小时前
探究Android Views、Flutter和Compose如何渲染你的UI
android·kotlin·android jetpack
Lesile1 天前
Android:Hilt框架入门 · 在ViewUI和ComposeUI下的应用
android·android jetpack
用户423816229071 天前
Android 16 WebView 页面顶部挖孔/通知栏不能显示UI问题排查
android
weixin_727535621 天前
Loop 已死,Graph 新生:AI 工作流的范式革命
android·人工智能·rxjava
严同学正在努力1 天前
从备份到恢复:我用 30 分钟恢复了误删的核心业务表
android·java·数据库·ai
梦想三三1 天前
LangChain Output Parser 实战:从字符串到结构化数据的完整指南
android·服务器·langchain·github·uv
GitLqr1 天前
深度拆解 Dart 事件循环:从面试题看清 Microtask 与 Event Queue 的执行顺序
flutter·面试·dart
爱笑鱼1 天前
Binder(八):远端进程死了,BinderProxy 为什么还能收到通知?
android