448 lines
9.9 KiB
Markdown
448 lines
9.9 KiB
Markdown
# 高级功能指南
|
||
|
||
本文档介绍 `yx_only_office_flutter` 插件的高级功能,这些功能基于对官方 Android 和 iOS 项目的深入研究实现。
|
||
|
||
## 概述
|
||
|
||
高级功能通过 `YxOnlyOfficeAdvancedViewer` 提供,包括:
|
||
|
||
- ✅ **WebViewController 直接访问** - 完全控制 WebView
|
||
- ✅ **编辑器方法调用** - 直接调用 DocsAPI 方法
|
||
- ✅ **图片插入** - 从相机/相册插入图片
|
||
- ✅ **文件下载** - 自动处理文件下载
|
||
- ✅ **生命周期管理** - 完整的编辑器生命周期事件
|
||
- ✅ **自定义 JavaScript 执行** - 执行任意 JS 代码
|
||
|
||
## 快速开始
|
||
|
||
### 基础使用
|
||
|
||
```dart
|
||
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 进行高级操作:
|
||
|
||
```dart
|
||
WebViewController? _controller;
|
||
|
||
YxOnlyOfficeAdvancedViewer(
|
||
serverUrl: serverUrl,
|
||
config: config,
|
||
onControllerReady: (controller) {
|
||
_controller = controller;
|
||
|
||
// 现在可以直接使用 WebViewController
|
||
controller.runJavaScript('console.log("Hello from Flutter!")');
|
||
},
|
||
)
|
||
```
|
||
|
||
### 2. 编辑器方法调用
|
||
|
||
直接调用 DocsAPI 提供的方法:
|
||
|
||
#### 插入图片
|
||
|
||
```dart
|
||
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}');
|
||
}
|
||
```
|
||
|
||
#### 下载文档
|
||
|
||
```dart
|
||
// 下载为 PDF
|
||
final result = await viewerKey.currentState!.downloadAs('pdf');
|
||
|
||
// 下载为 DOCX
|
||
final result = await viewerKey.currentState!.downloadAs('docx');
|
||
|
||
// 支持的格式: docx, pdf, txt, rtf, odt, html, epub 等
|
||
```
|
||
|
||
#### 设置审阅模式
|
||
|
||
```dart
|
||
// 启用审阅模式
|
||
await viewerKey.currentState!.setReviewerMode(true);
|
||
|
||
// 显示修订
|
||
await viewerKey.currentState!.showReviewChanges(true);
|
||
```
|
||
|
||
#### 销毁编辑器
|
||
|
||
```dart
|
||
await viewerKey.currentState!.destroyEditor();
|
||
```
|
||
|
||
#### 执行自定义 JavaScript
|
||
|
||
```dart
|
||
final result = await viewerKey.currentState!.executeJavaScript('''
|
||
if (window.docEditor) {
|
||
return window.docEditor.getDocumentName();
|
||
}
|
||
return null;
|
||
''');
|
||
|
||
print('文档名称: ${result.data}');
|
||
```
|
||
|
||
### 3. 图片插入功能
|
||
|
||
#### 方式 1:使用内置处理器
|
||
|
||
```dart
|
||
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:手动调用
|
||
|
||
```dart
|
||
// 选择图片
|
||
final image = await ImagePicker().pickImage(source: ImageSource.gallery);
|
||
final imageUrl = await uploadToServer(image!.path);
|
||
|
||
// 插入到文档
|
||
final result = await viewerKey.currentState!.insertImage(imageUrl);
|
||
```
|
||
|
||
### 4. 文件下载处理
|
||
|
||
自动拦截并处理文件下载请求:
|
||
|
||
```dart
|
||
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. 生命周期事件
|
||
|
||
完整的编辑器生命周期管理:
|
||
|
||
```dart
|
||
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
|
||
|
||
### 运行高级示例
|
||
|
||
```bash
|
||
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`:
|
||
|
||
```dart
|
||
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` |
|
||
|
||
## 高级配置
|
||
|
||
### 自定义图片上传
|
||
|
||
```dart
|
||
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;
|
||
}
|
||
```
|
||
|
||
### 自定义文件下载
|
||
|
||
```dart
|
||
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. 等待文档准备好
|
||
|
||
```dart
|
||
bool _isDocumentReady = false;
|
||
|
||
YxOnlyOfficeAdvancedViewer(
|
||
onDocumentReady: () {
|
||
setState(() => _isDocumentReady = true);
|
||
},
|
||
)
|
||
|
||
// 只在文档准备好后调用方法
|
||
if (_isDocumentReady) {
|
||
await viewerKey.currentState!.insertImage(url);
|
||
}
|
||
```
|
||
|
||
### 2. 错误处理
|
||
|
||
```dart
|
||
final result = await viewerKey.currentState!.downloadAs('pdf');
|
||
|
||
if (result.success) {
|
||
print('成功');
|
||
} else {
|
||
print('失败: ${result.error}');
|
||
// 显示错误提示给用户
|
||
}
|
||
```
|
||
|
||
### 3. 内存管理
|
||
|
||
```dart
|
||
@override
|
||
void dispose() {
|
||
// 在页面销毁时销毁编辑器
|
||
viewerKey.currentState?.destroyEditor();
|
||
super.dispose();
|
||
}
|
||
```
|
||
|
||
## 常见问题
|
||
|
||
### Q: 如何判断编辑器是否准备好?
|
||
|
||
A: 监听 `onDocumentReady` 回调:
|
||
|
||
```dart
|
||
bool _isReady = false;
|
||
|
||
onDocumentReady: () {
|
||
setState(() => _isReady = true);
|
||
}
|
||
```
|
||
|
||
### Q: 图片插入失败怎么办?
|
||
|
||
A: 检查:
|
||
1. 文档是否已准备好(`onDocumentReady` 已触发)
|
||
2. 图片 URL 是否可访问
|
||
3. 是否有编辑权限(`mode: 'edit'`)
|
||
|
||
### Q: 如何获取编辑器的当前状态?
|
||
|
||
A: 使用 `executeJavaScript`:
|
||
|
||
```dart
|
||
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
|
||
|
||
## 更多资源
|
||
|
||
- [基础功能文档](../README.md)
|
||
- [API 参考](API_REFERENCE.md)
|
||
- [快速开始](QUICK_START.md)
|
||
- [ONLYOFFICE 官方文档](https://api.onlyoffice.com/docs/docs-api/)
|
||
|
||
## 贡献
|
||
|
||
欢迎提交 Issue 和 Pull Request!如果您发现了 Bug 或有功能建议,请在 GitHub 上告诉我们。
|
||
|