【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),
              );
            },
          ),
        ],
      ),
    );
  }
}
相关推荐
事圆则缓14 分钟前
Kotlin 泛型方差实战:out、in、星投影与类型擦除
android·开发语言·kotlin
传奇开心果编程2 小时前
【现代声明式UI学与练】第4课 列表渲染与 key——如何高效渲染列表、key 的作用、列表重排时的状态保持
学习·flutter·react native·ui·swiftui·android jetpack
传奇开心果编程2 小时前
【Compose Multiplatform 跨端开发学与练】第5课 网络与数据层
android·网络·学习·ui·ios·kotlin·composer
ao-weilai3 小时前
MySQL数据库:内置函数
android·数据库·mysql
SWAGGY..3 小时前
【C++进阶】:(7)红黑树的原理与 C++ 实现:结构设计、插入调整及性质验证
android·java·开发语言·c++·算法
Android打工仔3 小时前
CoroutineScheduler 设计解析(上)—— 为什么 Dispatchers.IO 会创建更多线程?
android·kotlin·源码阅读
m0_738185825 小时前
Flutter 鸿蒙化实战:media_info 适配 OpenHarmony,媒体信息与缩略图
flutter·华为·harmonyos·鸿蒙·媒体
martindelophy5 小时前
用自然语言剪视频:我们在 Timeline Studio 中实现了 ChatCut 对话剪辑
android·音视频
ao-weilai5 小时前
MySQL数据库:复合查询
android·数据库·mysql
m0_738185826 小时前
Flutter 鸿蒙化实战:just_audio 适配 OpenHarmony,功能强大的播放器
flutter·华为·harmonyos·鸿蒙