You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter软键盘按键检测回调:自定义触觉振动实现遇阻求助

解决方案:通过iOS原生插件监听系统软键盘按键事件

Flutter框架本身没有提供直接监听系统软键盘按键按下的API,你之前尝试的FocusNode、RawKeyboardListener等方法仅对硬件键盘生效,系统软键盘的事件不会通过这些回调传递。要实现需求,必须通过自定义iOS原生插件来捕捉软键盘按键事件,再传递给Flutter触发振动。

具体实现步骤

1. 自定义Flutter插件(iOS端)

通过方法交换(Swizzling)Hook系统输入控件的按键事件,将事件传递给Flutter,也可以直接在原生端触发触觉振动。

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller = window?.rootViewController as! FlutterViewController
    let hapticChannel = FlutterMethodChannel(name: "com.yourdomain.haptic_keyboard", binaryMessenger: controller.binaryMessenger)
    
    // 交换UITextView的keyDown方法,捕捉按键事件
    let originalSelector = #selector(UITextView.keyDown(_:))
    let swizzledSelector = #selector(UITextView.swizzled_keyDown(_:))
    
    if let originalMethod = class_getInstanceMethod(UITextView.self, originalSelector),
       let swizzledMethod = class_getInstanceMethod(UITextView.self, swizzledSelector) {
      method_exchangeImplementations(originalMethod, swizzledMethod)
    }
    
    // 保存MethodChannel供交换后的方法调用
    HapticChannel.shared.channel = hapticChannel
    
    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

// 全局存储MethodChannel
class HapticChannel {
  static let shared = HapticChannel()
  var channel: FlutterMethodChannel?
}

extension UITextView {
  @objc func swizzled_keyDown(_ key: UIKey) {
    // 先调用原方法保证正常输入逻辑
    self.swizzled_keyDown(key)
    
    // 方式1:给Flutter发送事件,由Flutter触发振动
    HapticChannel.shared.channel?.invokeMethod("keyPressed", arguments: nil)
    
    // 方式2:直接在原生端触发触觉振动(延迟更低)
    let generator = UIImpactFeedbackGenerator(style: .light)
    generator.impactOccurred()
  }
}

2. Flutter端接收事件并触发振动

通过MethodChannel监听原生传递的按键事件,调用你的自定义振动插件。

import 'package:flutter/services.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final MethodChannel _hapticChannel = const MethodChannel('com.yourdomain.haptic_keyboard');
  final FocusNode _focusNode = FocusNode();

  @override
  void initState() {
    super.initState();
    // 监听原生传递的按键事件
    _hapticChannel.setMethodCallHandler((call) async {
      if (call.method == 'keyPressed') {
        // 调用你的自定义振动插件
        HapticsPlugin().click();
      }
    });
  }

  @override
  void dispose() {
    _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('软键盘触觉反馈')),
        body: Center(
          child: EditableText(
            focusNode: _focusNode,
            controller: TextEditingController(),
            style: const TextStyle(fontSize: 16),
            cursorColor: Colors.blue,
            backgroundCursorColor: Colors.grey,
          ),
        ),
      ),
    );
  }
}

// 你的自定义振动插件类
class HapticsPlugin {
  void click() {
    // 这里是你的振动实现逻辑
    print('触发触觉振动');
  }
}

关键说明

  • 方法交换(Swizzling)需谨慎:如果你的项目中有其他第三方库也修改了UITextView的keyDown方法,可能会产生冲突。可以添加判断,仅对Flutter专属的输入控件(如FlutterTextInputView)生效。
  • 原生端直接振动:在iOS端直接触发振动可以减少跨平台通信的延迟,体验更流畅。
  • 兼容性:上述代码适配iOS 13+,低版本需使用旧版keyDown(_ event: UIEvent?)方法。

内容的提问来源于stack exchange,提问作者Mikael Wills

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 17:13:17