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

基于Flutter开发macOS跨应用读写输入文本的可行方案问询

在Flutter中实现macOS跨应用文本读写(类似Grammarly Desktop)

核心方案:Flutter原生通道桥接macOS Accessibility API

Flutter本身没有直接封装跨应用文本操作的能力,必须通过原生通道(Method Channel)桥接macOS系统的Accessibility框架实现,无需完全转向原生开发。

步骤1:配置Accessibility权限

首先在macOS端的Info.plist中添加权限声明,否则应用无法申请到必要权限:

<key>NSAccessibilityUsageDescription</key>
<string>需要访问输入文本以提供语法修正服务</string>

应用启动时,需通过原生代码触发Accessibility权限申请弹窗,这一步要通过Method Channel调用原生逻辑完成。

步骤2:桥接原生Accessibility核心逻辑

在macOS原生端(Swift/Objective-C)实现跨应用文本读写的核心功能,再通过Method Channel暴露给Flutter层调用:

原生端关键代码示例(Swift)

import Cocoa
import FlutterMacOS
import Accessibility

class AccessibilityHandler {
    // 获取当前活跃输入框的文本内容
    func fetchCurrentInputText() -> String? {
        guard let focusedElement = AXUIElementCopyFocusedElement(AXUIElementCreateSystemWide()) else {
            return nil
        }
        var textValue: AnyObject?
        let status = AXUIElementCopyAttributeValue(focusedElement, kAXValueAttribute as CFString, &textValue)
        return status == .success ? textValue as? String : nil
    }

    // 修改当前活跃输入框的文本内容
    func updateCurrentInputText(_ newText: String) -> Bool {
        guard let focusedElement = AXUIElementCopyFocusedElement(AXUIElementCreateSystemWide()) else {
            return false
        }
        let status = AXUIElementSetAttributeValue(focusedElement, kAXValueAttribute as CFString, newText as CFTypeRef)
        return status == .success
    }
}

// 注册Method Channel
class MainFlutterWindow: NSWindow {
    override func awakeFromNib() {
        let flutterVC = FlutterViewController.init()
        self.contentViewController = flutterVC
        self.setFrame(self.frame, display: true)

        let channel = FlutterMethodChannel(name: "app/accessibility", binaryMessenger: flutterVC.engine.binaryMessenger)
        let handler = AccessibilityHandler()
        
        channel.setMethodCallHandler { call, result in
            switch call.method {
            case "getInputText":
                result(handler.fetchCurrentInputText())
            case "setInputText":
                guard let text = call.arguments as? String else {
                    result(FlutterError(code: "INVALID_ARG", message: "缺少文本参数", details: nil))
                    return
                }
                result(handler.updateCurrentInputText(text))
            default:
                result(FlutterMethodNotImplemented)
            }
        }

        super.awakeFromNib()
    }
}

Flutter端调用示例

import 'package:flutter/services.dart';

class AccessibilityService {
  static const _channel = MethodChannel('app/accessibility');

  static Future<String?> getCurrentInputText() async {
    try {
      return await _channel.invokeMethod('getInputText');
    } on PlatformException catch (e) {
      print("获取文本失败:${e.message}");
      return null;
    }
  }

  static Future<bool?> setCurrentInputText(String text) async {
    try {
      return await _channel.invokeMethod('setInputText', text);
    } on PlatformException catch (e) {
      print("设置文本失败:${e.message}");
      return false;
    }
  }
}

步骤3:权限状态监听与引导

在原生端通过AXIsProcessTrusted()检查权限状态,再通过Method Channel同步到Flutter层。当用户拒绝权限时,Flutter端可弹出提示,引导用户前往系统偏好设置的「辅助功能」面板开启权限。

注意事项

  • 部分应用(如Safari、Slack)有额外隐私限制,需确保你的应用被添加到系统辅助权限列表中
  • Accessibility API调用需处理多种异常场景,比如元素不可访问、权限不足等
  • 避免频繁监听输入框,建议按需触发操作(如用户点击修正按钮时再获取文本)

内容的提问来源于stack exchange,提问作者Renan Carvs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 22:05:19