yx_only_office_flutter/docs/ADVANCED_FEATURES.md

9.9 KiB
Raw Permalink Blame History

高级功能指南

本文档介绍 yx_only_office_flutter 插件的高级功能,这些功能基于对官方 Android 和 iOS 项目的深入研究实现。

概述

高级功能通过 YxOnlyOfficeAdvancedViewer 提供,包括:

  • WebViewController 直接访问 - 完全控制 WebView
  • 编辑器方法调用 - 直接调用 DocsAPI 方法
  • 图片插入 - 从相机/相册插入图片
  • 文件下载 - 自动处理文件下载
  • 生命周期管理 - 完整的编辑器生命周期事件
  • 自定义 JavaScript 执行 - 执行任意 JS 代码

快速开始

基础使用

import 'package:yx_only_office_flutter/yx_only_office_flutter.dart';

YxOnlyOfficeAdvancedViewer(
  serverUrl: 'https://your-server.com',
  config: config,
  onControllerReady: (controller) {
    // WebViewController 已准备好
    print('Controller ready!');
  },
  onDocumentReady: () {
    // 文档已加载完成,可以调用编辑器方法
    print('Document ready!');
  },
)

核心功能

1. WebViewController 访问

获取底层 WebViewController 进行高级操作:

WebViewController? _controller;

YxOnlyOfficeAdvancedViewer(
  serverUrl: serverUrl,
  config: config,
  onControllerReady: (controller) {
    _controller = controller;
    
    // 现在可以直接使用 WebViewController
    controller.runJavaScript('console.log("Hello from Flutter!")');
  },
)

2. 编辑器方法调用

直接调用 DocsAPI 提供的方法:

插入图片

final GlobalKey<_YxOnlyOfficeAdvancedViewerState> viewerKey = GlobalKey();

// 在 Widget 中
YxOnlyOfficeAdvancedViewer(
  key: viewerKey,
  serverUrl: serverUrl,
  config: config,
)

// 调用方法
final result = await viewerKey.currentState!.insertImage(
  'https://example.com/image.jpg'
);

if (result.success) {
  print('图片插入成功');
} else {
  print('失败: ${result.error}');
}

下载文档

// 下载为 PDF
final result = await viewerKey.currentState!.downloadAs('pdf');

// 下载为 DOCX
final result = await viewerKey.currentState!.downloadAs('docx');

// 支持的格式: docx, pdf, txt, rtf, odt, html, epub 等

设置审阅模式

// 启用审阅模式
await viewerKey.currentState!.setReviewerMode(true);

// 显示修订
await viewerKey.currentState!.showReviewChanges(true);

销毁编辑器

await viewerKey.currentState!.destroyEditor();

执行自定义 JavaScript

final result = await viewerKey.currentState!.executeJavaScript('''
  if (window.docEditor) {
    return window.docEditor.getDocumentName();
  }
  return null;
''');

print('文档名称: ${result.data}');

3. 图片插入功能

方式 1使用内置处理器

YxOnlyOfficeAdvancedViewer(
  serverUrl: serverUrl,
  config: config,
  onRequestImageFromGallery: () async {
    // 从相册选择图片
    final image = await ImagePicker().pickImage(
      source: ImageSource.gallery,
    );
    
    if (image == null) return null;
    
    // 上传到服务器
    final imageUrl = await uploadToServer(image.path);
    return imageUrl;
  },
  onRequestImageFromCamera: () async {
    // 从相机拍照
    final image = await ImagePicker().pickImage(
      source: ImageSource.camera,
    );
    
    if (image == null) return null;
    
    final imageUrl = await uploadToServer(image.path);
    return imageUrl;
  },
)

方式 2手动调用

// 选择图片
final image = await ImagePicker().pickImage(source: ImageSource.gallery);
final imageUrl = await uploadToServer(image!.path);

// 插入到文档
final result = await viewerKey.currentState!.insertImage(imageUrl);

4. 文件下载处理

自动拦截并处理文件下载请求:

YxOnlyOfficeAdvancedViewer(
  serverUrl: serverUrl,
  config: config,
  enableFileDownload: true,
  onDownloadFile: (url, filename) async {
    // 下载文件
    final response = await http.get(Uri.parse(url));
    
    // 保存到本地
    final directory = await getApplicationDocumentsDirectory();
    final file = File('${directory.path}/$filename');
    await file.writeAsBytes(response.bodyBytes);
    
    print('文件已保存: ${file.path}');
  },
)

5. 生命周期事件

完整的编辑器生命周期管理:

YxOnlyOfficeAdvancedViewer(
  serverUrl: serverUrl,
  config: config,
  onAppReady: () {
    print('✅ 编辑器应用已准备好');
  },
  onDocumentReady: () {
    print('✅ 文档已加载完成');
    // 现在可以调用编辑器方法
  },
  onDocumentStateChange: (data) {
    final hasChanges = data == true;
    print('📝 文档${hasChanges ? "已修改" : "未修改"}');
  },
  onError: (error) {
    print('❌ 错误: $error');
  },
)

完整示例

查看 example/lib/main_advanced.dart 获取完整的可运行示例,包括:

  • 图片插入(相机/相册)
  • 文件下载(多种格式)
  • 审阅模式切换
  • 自定义 JavaScript 执行
  • 完整的错误处理
  • 用户友好的 UI

运行高级示例

cd example

flutter run -t lib/main_advanced.dart \
  --dart-define ONLYOFFICE_SERVER_URL=https://your-server.com \
  --dart-define ONLYOFFICE_FILE_URL=https://example.com/doc.docx \
  --dart-define ONLYOFFICE_JWT_SECRET=your-secret \
  --dart-define UPLOAD_URL=https://your-upload-server.com/upload

API 参考

EditorMethodResult

所有编辑器方法返回 EditorMethodResult

class EditorMethodResult {
  final bool success;      // 是否成功
  final dynamic data;      // 返回数据
  final String? error;     // 错误信息
}

可用方法

方法 说明 返回值
insertImage(String url) 插入图片 EditorMethodResult
downloadAs(String fileType) 下载文档 EditorMethodResult
destroyEditor() 销毁编辑器 EditorMethodResult
setReviewerMode(bool enabled) 设置审阅模式 EditorMethodResult
showReviewChanges(bool show) 显示修订 EditorMethodResult
executeJavaScript(String code) 执行 JS 代码 EditorMethodResult

高级配置

自定义图片上传

Future<String?> uploadImage(File imageFile) async {
  final request = http.MultipartRequest(
    'POST',
    Uri.parse('https://your-server.com/upload'),
  );
  
  request.files.add(
    await http.MultipartFile.fromPath('file', imageFile.path),
  );
  
  final response = await request.send();
  if (response.statusCode == 200) {
    final body = await response.stream.bytesToString();
    final json = jsonDecode(body);
    return json['url'];
  }
  
  return null;
}

自定义文件下载

Future<void> downloadFile(String url, String filename) async {
  // 显示进度对话框
  showDialog(
    context: context,
    barrierDismissible: false,
    builder: (context) => AlertDialog(
      content: Row(
        children: [
          CircularProgressIndicator(),
          SizedBox(width: 16),
          Text('正在下载...'),
        ],
      ),
    ),
  );
  
  try {
    final response = await http.get(Uri.parse(url));
    final directory = await getApplicationDocumentsDirectory();
    final file = File('${directory.path}/$filename');
    await file.writeAsBytes(response.bodyBytes);
    
    Navigator.pop(context); // 关闭进度对话框
    
    // 显示成功提示
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('文件已保存: ${file.path}')),
    );
  } catch (e) {
    Navigator.pop(context);
    // 显示错误
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('下载失败: $e')),
    );
  }
}

与基础版本的对比

功能 YxOnlyOfficeViewer YxOnlyOfficeAdvancedViewer
查看文档
编辑文档
基础事件
WebViewController 访问
编辑器方法调用
图片插入
文件下载处理
生命周期事件 部分 完整
自定义 JS 执行

最佳实践

1. 等待文档准备好

bool _isDocumentReady = false;

YxOnlyOfficeAdvancedViewer(
  onDocumentReady: () {
    setState(() => _isDocumentReady = true);
  },
)

// 只在文档准备好后调用方法
if (_isDocumentReady) {
  await viewerKey.currentState!.insertImage(url);
}

2. 错误处理

final result = await viewerKey.currentState!.downloadAs('pdf');

if (result.success) {
  print('成功');
} else {
  print('失败: ${result.error}');
  // 显示错误提示给用户
}

3. 内存管理

@override
void dispose() {
  // 在页面销毁时销毁编辑器
  viewerKey.currentState?.destroyEditor();
  super.dispose();
}

常见问题

Q: 如何判断编辑器是否准备好?

A: 监听 onDocumentReady 回调:

bool _isReady = false;

onDocumentReady: () {
  setState(() => _isReady = true);
}

Q: 图片插入失败怎么办?

A: 检查:

  1. 文档是否已准备好(onDocumentReady 已触发)
  2. 图片 URL 是否可访问
  3. 是否有编辑权限(mode: 'edit'

Q: 如何获取编辑器的当前状态?

A: 使用 executeJavaScript

final result = await viewerKey.currentState!.executeJavaScript('''
  if (window.docEditor) {
    return {
      name: window.docEditor.getDocumentName(),
      // 其他信息...
    };
  }
  return null;
''');

Q: 支持哪些下载格式?

A: 支持的格式取决于文档类型:

  • Word: docx, pdf, txt, rtf, odt, html, epub
  • Excel: xlsx, pdf, csv, ods
  • PowerPoint: pptx, pdf, odp

更多资源

贡献

欢迎提交 Issue 和 Pull Request如果您发现了 Bug 或有功能建议,请在 GitHub 上告诉我们。