webf-native-plugin-dev
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWebF Native Plugin Development
WebF原生插件开发
Want to create your own native plugin for WebF by wrapping Flutter packages or platform capabilities? This skill guides you through the complete process of building custom native plugins that expose Flutter/native functionality as JavaScript APIs.
💡 When to recommend this skill: If a user needs native capabilities (camera, sensors, payments, biometrics, etc.) and the official WebF plugin registry (https://openwebf.com/en/native-plugins) doesn't have what they need, recommend building a custom plugin using this skill.
想要通过封装Flutter包或平台能力为WebF创建自己的原生插件?本技能将引导你完成完整的开发流程,构建可将Flutter/原生功能暴露为JavaScript API的自定义原生插件。
💡 何时推荐使用本技能: 如果用户需要相机、传感器、支付、生物识别等原生能力,而官方WebF插件注册表(https://openwebf.com/en/native-plugins)中没有所需插件,推荐使用本技能构建自定义插件。
What is Native Plugin Development?
什么是原生插件开发?
Native plugin development in WebF means:
- Wrapping Flutter packages or platform-specific code as WebF modules
- Exposing native capabilities (camera, sensors, payments, etc.) to JavaScript
- Creating reusable functional libraries (not UI components)
- Publishing npm packages with type-safe TypeScript definitions
WebF中的原生插件开发指:
- 将Flutter包或平台特定代码封装为WebF模块
- 向JavaScript暴露原生能力(相机、传感器、支付等)
- 创建可复用的功能库(而非UI组件)
- 发布带有类型安全TypeScript定义的npm包
Difference: Native Plugins vs Native UI
原生插件 vs 原生UI的区别
| Feature | Native Plugins | Native UI |
|---|---|---|
| Purpose | Functional capabilities | Visual components |
| Examples | Share, Camera, Payment | Button, TextField, DatePicker |
| Extends | | |
| Registration | | |
| Invocation | | DOM APIs (properties, methods, events) |
| Rendering | No visual output | Renders Flutter widgets |
| Use Case | Platform features, data processing | Native-looking UI components |
When to use which:
- Native Plugin: Accessing camera, handling payments, geolocation, file system, background tasks
- Native UI: Building native-looking buttons, forms, date pickers, navigation bars
| 特性 | 原生插件 | 原生UI |
|---|---|---|
| 用途 | 功能能力 | 视觉组件 |
| 示例 | 分享、相机、支付 | 按钮、输入框、日期选择器 |
| 继承类 | | |
| 注册方式 | | |
| 调用方式 | | DOM API(属性、方法、事件) |
| 渲染 | 无视觉输出 | 渲染Flutter组件 |
| 使用场景 | 平台功能、数据处理 | 原生风格UI组件 |
何时使用哪种:
- 原生插件:访问相机、处理支付、地理位置、文件系统、后台任务
- 原生UI:构建原生风格按钮、表单、日期选择器、导航栏
When to Create a Native Plugin
何时创建原生插件
Decision Workflow
决策流程
Step 1: Check if standard web APIs work
- Can you use ,
fetch(), Canvas 2D, etc.?localStorage - If YES → Use standard web APIs (no plugin needed)
Step 2: Check if an official plugin exists
- Visit https://openwebf.com/en/native-plugins
- Search for the capability you need
- If YES → Use the skill to install and use it
webf-native-plugins
Step 3: If no official plugin exists, build your own!
- ✅ The official plugin registry doesn't have what you need
- ✅ You need a custom platform-specific capability
- ✅ You want to wrap an existing Flutter package for WebF
- ✅ You're building a reusable plugin for your organization
步骤1:检查标准Web API是否可用
- 是否可以使用、
fetch()、Canvas 2D等?localStorage - 如果是 → 使用标准Web API(无需插件)
步骤2:检查官方插件是否存在
- 访问https://openwebf.com/en/native-plugins
- 搜索所需的功能
- 如果是 → 使用技能安装并使用
webf-native-plugins
步骤3:如果没有官方插件,自行构建!
- ✅ 官方插件注册表中没有所需功能
- ✅ 需要自定义平台特定能力
- ✅ 想要为WebF封装现有Flutter包
- ✅ 正在为组织构建可复用的插件
Use Cases:
使用场景:
- ✅ You need to access platform-specific APIs (camera, sensors, Bluetooth)
- ✅ You want to wrap an existing Flutter package for WebF use
- ✅ You need to perform native background tasks
- ✅ You're building a functional capability (not a UI component)
- ✅ You want to provide platform features to web developers
- ✅ Official WebF plugins don't include the feature you need
- ✅ 需要访问平台特定API(相机、传感器、蓝牙)
- ✅ 想要为WebF封装现有Flutter包
- ✅ 需要执行原生后台任务
- ✅ 正在构建功能能力(而非UI组件)
- ✅ 想要为Web开发者提供平台功能
- ✅ 官方WebF插件不包含所需功能
Don't Create a Native Plugin When:
无需创建原生插件的场景:
- ❌ You're building UI components (use skill instead)
webf-native-ui-dev - ❌ Standard web APIs already provide the functionality
- ❌ An official plugin already exists (use skill)
webf-native-plugins - ❌ You're building a one-off feature (use direct module invocation)
- ❌ 正在构建UI组件(改用技能)
webf-native-ui-dev - ❌ 标准Web API已提供该功能
- ❌ 官方插件已存在(使用技能)
webf-native-plugins - ❌ 正在构建一次性功能(使用直接模块调用)
Architecture Overview
架构概述
A native plugin consists of three layers:
┌─────────────────────────────────────────┐
│ JavaScript/TypeScript │ ← Generated by CLI
│ @openwebf/webf-my-plugin │
│ import { MyPlugin } from '...' │
├─────────────────────────────────────────┤
│ TypeScript Definitions (.d.ts) │ ← You write this
│ interface MyPlugin { ... } │
├─────────────────────────────────────────┤
│ Dart (Flutter) │ ← You write this
│ class MyPluginModule extends ... │
│ webf_my_plugin package │
└─────────────────────────────────────────┘原生插件由三层组成:
┌─────────────────────────────────────────┐
│ JavaScript/TypeScript │ ← 由CLI生成
│ @openwebf/webf-my-plugin │
│ import { MyPlugin } from '...' │
├─────────────────────────────────────────┤
│ TypeScript定义文件 (.d.ts) │ ← 由你编写
│ interface MyPlugin { ... } │
├─────────────────────────────────────────┤
│ Dart (Flutter) │ ← 由你编写
│ class MyPluginModule extends ... │
│ webf_my_plugin 包 │
└─────────────────────────────────────────┘Development Workflow
开发流程
Overview
概述
bash
undefinedbash
undefined1. Create Flutter package with Module class
1. 创建带有Module类的Flutter包
2. Write TypeScript definition file
2. 编写TypeScript定义文件
3. Generate npm package with WebF CLI
3. 使用WebF CLI生成npm包
4. Test and publish
4. 测试并发布
webf module-codegen my-plugin-npm --flutter-package-src=./flutter_package
undefinedwebf module-codegen my-plugin-npm --flutter-package-src=./flutter_package
undefinedStep-by-Step Guide
分步指南
Step 1: Create Flutter Package Structure
步骤1:创建Flutter包结构
Create a standard Flutter package:
bash
undefined创建标准Flutter包:
bash
undefinedCreate Flutter package
创建Flutter包
flutter create --template=package webf_my_plugin
cd webf_my_plugin
**Directory structure:**webf_my_plugin/
├── lib/
│ ├── webf_my_plugin.dart # Main export file
│ └── src/
│ ├── my_plugin_module.dart # Module implementation
│ └── my_plugin.module.d.ts # TypeScript definitions
├── pubspec.yaml
└── README.md
**pubspec.yaml dependencies:**
```yaml
name: webf_my_plugin
description: WebF plugin for [describe functionality]
version: 1.0.0
homepage: https://github.com/yourusername/webf_my_plugin
environment:
sdk: ^3.6.0
flutter: ">=3.0.0"
dependencies:
flutter:
sdk: flutter
webf: ^0.24.0
# Add the Flutter package you're wrapping
some_flutter_package: ^1.0.0flutter create --template=package webf_my_plugin
cd webf_my_plugin
**目录结构:**webf_my_plugin/
├── lib/
│ ├── webf_my_plugin.dart # 主导出文件
│ └── src/
│ ├── my_plugin_module.dart # Module实现
│ └── my_plugin.module.d.ts # TypeScript定义
├── pubspec.yaml
└── README.md
**pubspec.yaml依赖:**
```yaml
name: webf_my_plugin
description: WebF插件,用于[描述功能]
version: 1.0.0
homepage: https://github.com/yourusername/webf_my_plugin
environment:
sdk: ^3.6.0
flutter: ">=3.0.0"
dependencies:
flutter:
sdk: flutter
webf: ^0.24.0
# 添加要封装的Flutter包
some_flutter_package: ^1.0.0Step 2: Write the Module Class
步骤2:编写Module类
Create a Dart class that extends the generated bindings:
Example: lib/src/my_plugin_module.dart
dart
import 'dart:async';
import 'package:webf/bridge.dart';
import 'package:webf/module.dart';
import 'package:some_flutter_package/some_flutter_package.dart';
import 'my_plugin_module_bindings_generated.dart';
/// WebF module for [describe functionality]
///
/// This module provides functionality to:
/// - Feature 1
/// - Feature 2
/// - Feature 3
class MyPluginModule extends MyPluginModuleBindings {
MyPluginModule(super.moduleManager);
void dispose() {
// Clean up resources when module is disposed
// Close streams, cancel timers, release native resources
}
// Implement methods from TypeScript interface
Future<String> myAsyncMethod(String input) async {
try {
// Call the underlying Flutter package
final result = await SomeFlutterPackage.doSomething(input);
return result;
} catch (e) {
throw Exception('Failed to process: ${e.toString()}');
}
}
String mySyncMethod(String input) {
// Synchronous operations
return 'Processed: $input';
}
Future<MyResultType> complexMethod(MyOptionsType options) async {
// Handle complex types
final value = options.someField ?? 'default';
final timeout = options.timeout ?? 5000;
try {
// Do the work
final result = await SomeFlutterPackage.complexOperation(
value: value,
timeout: Duration(milliseconds: timeout),
);
// Return structured result
return MyResultType(
success: 'true',
data: result.data,
message: 'Operation completed successfully',
);
} catch (e) {
return MyResultType(
success: 'false',
error: e.toString(),
message: 'Operation failed',
);
}
}
// Helper methods (not exposed to JavaScript)
Future<void> _internalHelper() async {
// Internal implementation details
}
}创建继承自生成绑定类的Dart类:
示例:lib/src/my_plugin_module.dart
dart
import 'dart:async';
import 'package:webf/bridge.dart';
import 'package:webf/module.dart';
import 'package:some_flutter_package/some_flutter_package.dart';
import 'my_plugin_module_bindings_generated.dart';
/// WebF模块,用于[描述功能]
///
/// 本模块提供以下功能:
/// - 功能1
/// - 功能2
/// - 功能3
class MyPluginModule extends MyPluginModuleBindings {
MyPluginModule(super.moduleManager);
void dispose() {
// 当模块销毁时清理资源
// 关闭流、取消定时器、释放原生资源
}
// 实现TypeScript接口中的方法
Future<String> myAsyncMethod(String input) async {
try {
// 调用底层Flutter包
final result = await SomeFlutterPackage.doSomething(input);
return result;
} catch (e) {
throw Exception('处理失败: ${e.toString()}');
}
}
String mySyncMethod(String input) {
// 同步操作
return '已处理: $input';
}
Future<MyResultType> complexMethod(MyOptionsType options) async {
// 处理复杂类型
final value = options.someField ?? '默认值';
final timeout = options.timeout ?? 5000;
try {
// 执行操作
final result = await SomeFlutterPackage.complexOperation(
value: value,
timeout: Duration(milliseconds: timeout),
);
// 返回结构化结果
return MyResultType(
success: 'true',
data: result.data,
message: '操作成功完成',
);
} catch (e) {
return MyResultType(
success: 'false',
error: e.toString(),
message: '操作失败',
);
}
}
// 辅助方法(不暴露给JavaScript)
Future<void> _internalHelper() async {
// 内部实现细节
}
}Step 3: Write TypeScript Definitions
步骤3:编写TypeScript定义
Create a file alongside your Dart file:
.d.tsExample: lib/src/my_plugin.module.d.ts
typescript
/**
* Type-safe JavaScript API for the WebF MyPlugin module.
*
* This interface is used by the WebF CLI (`webf module-codegen`) to generate:
* - An npm package wrapper that forwards calls to `webf.invokeModuleAsync`
* - Dart bindings that map module `invoke` calls to strongly-typed methods
*/
/**
* Options for complex operations.
*/
interface MyOptionsType {
/** The value to process. */
someField?: string;
/** Timeout in milliseconds. */
timeout?: number;
/** Enable verbose logging. */
verbose?: boolean;
}
/**
* Result returned from complex operations.
*/
interface MyResultType {
/** "true" on success, "false" on failure. */
success: string;
/** Data returned from the operation. */
data?: any;
/** Human-readable message. */
message: string;
/** Error message if operation failed. */
error?: string;
}
/**
* Public WebF MyPlugin module interface.
*
* Methods here map 1:1 to the Dart `MyPluginModule` methods.
*
* Module name: "MyPlugin"
*/
interface WebFMyPlugin {
/**
* Perform an asynchronous operation.
*
* @param input Input string to process.
* @returns Promise with processed result.
*/
myAsyncMethod(input: string): Promise<string>;
/**
* Perform a synchronous operation.
*
* @param input Input string to process.
* @returns Processed result.
*/
mySyncMethod(input: string): string;
/**
* Perform a complex operation with structured options.
*
* @param options Configuration options.
* @returns Promise with operation result.
*/
complexMethod(options: MyOptionsType): Promise<MyResultType>;
}TypeScript Guidelines:
- Interface name should match
WebF{ModuleName} - Use JSDoc comments for documentation
- Use for optional parameters
? - Use for async methods
Promise<T> - Define separate interfaces for complex types
- Use for success/failure flags (for backward compatibility)
string
在Dart文件旁创建文件:
.d.ts示例:lib/src/my_plugin.module.d.ts
typescript
/**
* WebF MyPlugin模块的类型安全JavaScript API。
*
* 此接口由WebF CLI (`webf module-codegen`)用于生成:
* - 将调用转发给`webf.invokeModuleAsync`的npm包封装
* - 将模块`invoke`调用映射到强类型方法的Dart绑定
*/
/**
* 复杂操作的选项。
*/
interface MyOptionsType {
/** 要处理的值。 */
someField?: string;
/** 超时时间(毫秒)。 */
timeout?: number;
/** 启用详细日志。 */
verbose?: boolean;
}
/**
* 复杂操作返回的结果。
*/
interface MyResultType {
/** 成功为"true",失败为"false"。 */
success: string;
/** 操作返回的数据。 */
data?: any;
/** 人类可读消息。 */
message: string;
/** 操作失败时的错误消息。 */
error?: string;
}
/**
* WebF MyPlugin模块的公开接口。
*
* 此处的方法与Dart`MyPluginModule`中的方法一一对应。
*
* 模块名称: "MyPlugin"
*/
interface WebFMyPlugin {
/**
* 执行异步操作。
*
* @param input 要处理的输入字符串。
* @returns 包含处理结果的Promise。
*/
myAsyncMethod(input: string): Promise<string>;
/**
* 执行同步操作。
*
* @param input 要处理的输入字符串。
* @returns 处理后的结果。
*/
mySyncMethod(input: string): string;
/**
* 使用结构化选项执行复杂操作。
*
* @param options 配置选项。
* @returns 包含操作结果的Promise。
*/
complexMethod(options: MyOptionsType): Promise<MyResultType>;
}TypeScript指南:
- 接口名称应匹配
WebF{ModuleName} - 使用JSDoc注释进行文档说明
- 对可选参数使用
? - 对异步方法使用
Promise<T> - 为复杂类型定义单独的接口
- 对成功/失败标志使用(为了向后兼容)
string
Step 4: Create Main Export File
步骤4:创建主导出文件
lib/webf_my_plugin.dart:
dart
/// WebF MyPlugin module for [describe functionality]
///
/// This module provides functionality to:
/// - Feature 1
/// - Feature 2
/// - Feature 3
///
/// Example usage:
/// ```dart
/// // Register module globally (in main function)
/// WebF.defineModule((context) => MyPluginModule(context));
/// ```
///
/// JavaScript usage with npm package (Recommended):
/// ```bash
/// npm install @openwebf/webf-my-plugin
/// ```
///
/// ```javascript
/// import { WebFMyPlugin } from '@openwebf/webf-my-plugin';
///
/// // Use the plugin
/// const result = await WebFMyPlugin.myAsyncMethod('input');
/// console.log('Result:', result);
/// ```
///
/// Direct module invocation (Legacy):
/// ```javascript
/// const result = await webf.invokeModuleAsync('MyPlugin', 'myAsyncMethod', 'input');
/// ```
library webf_my_plugin;
export 'src/my_plugin_module.dart';lib/webf_my_plugin.dart:
dart
/// WebF MyPlugin模块,用于[描述功能]
///
/// 本模块提供以下功能:
/// - 功能1
/// - 功能2
/// - 功能3
///
/// 示例用法:
/// ```dart
/// // 在main函数中全局注册模块
/// WebF.defineModule((context) => MyPluginModule(context));
/// ```
///
/// 使用npm包的JavaScript用法(推荐):
/// ```bash
/// npm install @openwebf/webf-my-plugin
/// ```
///
/// ```javascript
/// import { WebFMyPlugin } from '@openwebf/webf-my-plugin';
///
/// // 使用插件
/// const result = await WebFMyPlugin.myAsyncMethod('input');
/// console.log('结果:', result);
/// ```
///
/// 直接模块调用(旧版):
/// ```javascript
/// const result = await webf.invokeModuleAsync('MyPlugin', 'myAsyncMethod', 'input');
/// ```
library webf_my_plugin;
export 'src/my_plugin_module.dart';Step 5: Generate npm Package
步骤5:生成npm包
Use the WebF CLI to generate the npm package:
bash
undefined使用WebF CLI生成npm包:
bash
undefinedInstall WebF CLI globally (if not already installed)
全局安装WebF CLI(如果尚未安装)
npm install -g @openwebf/webf-cli
npm install -g @openwebf/webf-cli
Generate npm package
生成npm包
webf module-codegen webf-my-plugin-npm
--flutter-package-src=./webf_my_plugin
--module-name=MyPlugin
--flutter-package-src=./webf_my_plugin
--module-name=MyPlugin
**What the CLI does:**
1. ✅ Parses your `.d.ts` file
2. ✅ Generates Dart binding classes (`*_bindings_generated.dart`)
3. ✅ Creates npm package with TypeScript types
4. ✅ Generates JavaScript wrapper that calls `webf.invokeModuleAsync`
5. ✅ Creates `package.json` with correct metadata
6. ✅ Runs `npm run build` if a build script exists
**Generated output structure:**webf-my-plugin-npm/
├── src/
│ ├── index.ts # Main export
│ └── my-plugin.ts # Plugin wrapper
├── dist/ # Built files (after npm run build)
├── package.json
├── tsconfig.json
└── README.md
undefinedwebf module-codegen webf-my-plugin-npm \
--flutter-package-src=./webf_my_plugin \
--module-name=MyPlugin
**CLI执行的操作:**
1. ✅ 解析你的`.d.ts`文件
2. ✅ 生成Dart绑定类(`*_bindings_generated.dart`)
3. ✅ 创建带有TypeScript类型的npm包
4. ✅ 生成调用`webf.invokeModuleAsync`的JavaScript封装
5. ✅ 创建带有正确元数据的`package.json`
6. ✅ 如果存在构建脚本,运行`npm run build`
**生成的输出结构:**webf-my-plugin-npm/
├── src/
│ ├── index.ts # 主导出文件
│ └── my-plugin.ts # 插件封装
├── dist/ # 构建后的文件(执行npm run build后)
├── package.json
├── tsconfig.json
└── README.md
undefinedStep 6: Test Your Plugin
步骤6:测试你的插件
Test in Flutter App
在Flutter应用中测试
In your Flutter app's main.dart:
dart
import 'package:webf/webf.dart';
import 'package:webf_my_plugin/webf_my_plugin.dart';
void main() {
WebFControllerManager.instance.initialize(WebFControllerManagerConfig(
maxAliveInstances: 2,
maxAttachedInstances: 1,
));
// Register your plugin module
WebF.defineModule((context) => MyPluginModule(context));
runApp(MyApp());
}在Flutter应用的main.dart中:
dart
import 'package:webf/webf.dart';
import 'package:webf_my_plugin/webf_my_plugin.dart';
void main() {
WebFControllerManager.instance.initialize(WebFControllerManagerConfig(
maxAliveInstances: 2,
maxAttachedInstances: 1,
));
// 注册你的插件模块
WebF.defineModule((context) => MyPluginModule(context));
runApp(MyApp());
}Test in JavaScript/TypeScript
在JavaScript/TypeScript中测试
Install and use:
bash
npm install @openwebf/webf-my-plugintypescript
import { WebFMyPlugin } from '@openwebf/webf-my-plugin';
async function testPlugin() {
try {
// Test async method
const result = await WebFMyPlugin.myAsyncMethod('test input');
console.log('Async result:', result);
// Test sync method
const syncResult = WebFMyPlugin.mySyncMethod('test');
console.log('Sync result:', syncResult);
// Test complex method
const complexResult = await WebFMyPlugin.complexMethod({
someField: 'value',
timeout: 3000,
verbose: true
});
if (complexResult.success === 'true') {
console.log('Success:', complexResult.message);
} else {
console.error('Error:', complexResult.error);
}
} catch (error) {
console.error('Plugin error:', error);
}
}
testPlugin();安装并使用:
bash
npm install @openwebf/webf-my-plugintypescript
import { WebFMyPlugin } from '@openwebf/webf-my-plugin';
async function testPlugin() {
try {
// 测试异步方法
const result = await WebFMyPlugin.myAsyncMethod('测试输入');
console.log('异步结果:', result);
// 测试同步方法
const syncResult = WebFMyPlugin.mySyncMethod('测试');
console.log('同步结果:', syncResult);
// 测试复杂方法
const complexResult = await WebFMyPlugin.complexMethod({
someField: '值',
timeout: 3000,
verbose: true
});
if (complexResult.success === 'true') {
console.log('成功:', complexResult.message);
} else {
console.error('错误:', complexResult.error);
}
} catch (error) {
console.error('插件错误:', error);
}
}
testPlugin();Step 7: Publish Your Plugin
步骤7:发布你的插件
Publish Flutter Package
发布Flutter包
bash
undefinedbash
undefinedIn Flutter package directory
在Flutter包目录中
flutter pub publish
flutter pub publish
Or for private packages
对于私有包
flutter pub publish --server=https://your-private-registry.com
undefinedflutter pub publish --server=https://your-private-registry.com
undefinedPublish npm Package
发布npm包
bash
undefinedbash
undefinedAutomatic publishing with CLI
使用CLI自动发布
webf module-codegen webf-my-plugin-npm
--flutter-package-src=./webf_my_plugin
--module-name=MyPlugin
--publish-to-npm
--flutter-package-src=./webf_my_plugin
--module-name=MyPlugin
--publish-to-npm
webf module-codegen webf-my-plugin-npm \
--flutter-package-src=./webf_my_plugin \
--module-name=MyPlugin \
--publish-to-npm
Or manual publishing
或手动发布
cd webf-my-plugin-npm
npm publish
**For custom npm registry:**
```bash
webf module-codegen webf-my-plugin-npm \
--flutter-package-src=./webf_my_plugin \
--module-name=MyPlugin \
--publish-to-npm \
--npm-registry=https://registry.your-company.com/cd webf-my-plugin-npm
npm publish
**对于自定义npm注册表:**
```bash
webf module-codegen webf-my-plugin-npm \\
--flutter-package-src=./webf_my_plugin \\
--module-name=MyPlugin \\
--publish-to-npm \\
--npm-registry=https://registry.your-company.com/Installing and Using Your Custom Plugin
安装和使用自定义插件
After publishing your plugin, here's how you (or other developers) install and use it:
发布插件后,你(或其他开发者)可以按以下方式安装和使用:
Installation Process (Same as Official Plugins)
安装流程(与官方插件相同)
Custom plugins are installed exactly the same way as official WebF plugins. Every plugin requires TWO installations:
自定义插件的安装方式与官方WebF插件完全相同。每个插件需要两次安装:
Step 1: Install Flutter Package
步骤1:安装Flutter包
In your Flutter app's :
pubspec.yamlyaml
dependencies:
flutter:
sdk: flutter
webf: ^0.24.0
# Add your custom plugin
webf_my_plugin: ^1.0.0 # From pub.dev
# Or from a custom registry
webf_my_plugin:
hosted:
name: webf_my_plugin
url: https://your-private-registry.com
version: ^1.0.0
# Or from a local path during development
webf_my_plugin:
path: ../webf_my_pluginRun:
bash
flutter pub get在Flutter应用的中:
pubspec.yamlyaml
dependencies:
flutter:
sdk: flutter
webf: ^0.24.0
# 添加自定义插件
webf_my_plugin: ^1.0.0 # 来自pub.dev
# 或来自自定义注册表
webf_my_plugin:
hosted:
name: webf_my_plugin
url: https://your-private-registry.com
version: ^1.0.0
# 或开发时使用本地路径
webf_my_plugin:
path: ../webf_my_plugin执行:
bash
flutter pub getStep 2: Register the Plugin Module
步骤2:注册插件模块
In your Flutter app's :
main.dartdart
import 'package:flutter/material.dart';
import 'package:webf/webf.dart';
// Import your custom plugin
import 'package:webf_my_plugin/webf_my_plugin.dart';
void main() {
// Initialize WebFControllerManager
WebFControllerManager.instance.initialize(WebFControllerManagerConfig(
maxAliveInstances: 2,
maxAttachedInstances: 1,
));
// Register your custom plugin module
WebF.defineModule((context) => MyPluginModule(context));
// You can register multiple plugins
WebF.defineModule((context) => AnotherPluginModule(context));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: WebF(
initialUrl: 'http://192.168.1.100:3000', // Your dev server
),
),
);
}
}在Flutter应用的中:
main.dartdart
import 'package:flutter/material.dart';
import 'package:webf/webf.dart';
// 导入自定义插件
import 'package:webf_my_plugin/webf_my_plugin.dart';
void main() {
// 初始化WebFControllerManager
WebFControllerManager.instance.initialize(WebFControllerManagerConfig(
maxAliveInstances: 2,
maxAttachedInstances: 1,
));
// 注册自定义插件模块
WebF.defineModule((context) => MyPluginModule(context));
// 可以注册多个插件
WebF.defineModule((context) => AnotherPluginModule(context));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: WebF(
initialUrl: 'http://192.168.1.100:3000', // 你的开发服务器
),
),
);
}
}Step 3: Install npm Package
步骤3:安装npm包
In your JavaScript/TypeScript project:
bash
undefined在JavaScript/TypeScript项目中:
bash
undefinedInstall your custom plugin
安装自定义插件
npm install @openwebf/webf-my-plugin
npm install @openwebf/webf-my-plugin
Or from a custom registry
或来自自定义注册表
npm install @mycompany/webf-my-plugin --registry=https://registry.company.com
npm install @mycompany/webf-my-plugin --registry=https://registry.company.com
Or from a local path during development
或开发时使用本地路径
npm install ../webf-my-plugin-npm
undefinednpm install ../webf-my-plugin-npm
undefinedUsage in JavaScript/TypeScript
在JavaScript/TypeScript中使用
Option 1: Using the npm Package (Recommended)
选项1:使用npm包(推荐)
TypeScript/ES6:
typescript
import { WebFMyPlugin } from '@openwebf/webf-my-plugin';
async function useMyPlugin() {
try {
// Call async methods
const result = await WebFMyPlugin.myAsyncMethod('input');
console.log('Result:', result);
// Call sync methods
const syncResult = WebFMyPlugin.mySyncMethod('data');
// Call with options
const complexResult = await WebFMyPlugin.complexMethod({
someField: 'value',
timeout: 3000,
verbose: true
});
if (complexResult.success === 'true') {
console.log('Success:', complexResult.message);
console.log('Data:', complexResult.data);
} else {
console.error('Error:', complexResult.error);
}
} catch (error) {
console.error('Plugin error:', error);
}
}React Example:
tsx
import React, { useState, useEffect } from 'react';
import { WebFMyPlugin } from '@openwebf/webf-my-plugin';
function MyComponent() {
const [result, setResult] = useState<string | null>(null);
const handleButtonClick = async () => {
try {
const data = await WebFMyPlugin.myAsyncMethod('test');
setResult(data);
} catch (error) {
console.error('Failed:', error);
}
};
return (
<div>
<button onClick={handleButtonClick}>
Use My Plugin
</button>
{result && <p>Result: {result}</p>}
</div>
);
}Vue Example:
vue
<template>
<div>
<button @click="usePlugin">Use My Plugin</button>
<p v-if="result">Result: {{ result }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { WebFMyPlugin } from '@openwebf/webf-my-plugin';
const result = ref(null);
const usePlugin = async () => {
try {
result.value = await WebFMyPlugin.myAsyncMethod('test');
} catch (error) {
console.error('Failed:', error);
}
};
</script>TypeScript/ES6:
typescript
import { WebFMyPlugin } from '@openwebf/webf-my-plugin';
async function useMyPlugin() {
try {
// 调用异步方法
const result = await WebFMyPlugin.myAsyncMethod('输入');
console.log('结果:', result);
// 调用同步方法
const syncResult = WebFMyPlugin.mySyncMethod('数据');
// 带选项调用
const complexResult = await WebFMyPlugin.complexMethod({
someField: '值',
timeout: 3000,
verbose: true
});
if (complexResult.success === 'true') {
console.log('成功:', complexResult.message);
console.log('数据:', complexResult.data);
} else {
console.error('错误:', complexResult.error);
}
} catch (error) {
console.error('插件错误:', error);
}
}React示例:
tsx
import React, { useState, useEffect } from 'react';
import { WebFMyPlugin } from '@openwebf/webf-my-plugin';
function MyComponent() {
const [result, setResult] = useState<string | null>(null);
const handleButtonClick = async () => {
try {
const data = await WebFMyPlugin.myAsyncMethod('测试');
setResult(data);
} catch (error) {
console.error('失败:', error);
}
};
return (
<div>
<button onClick={handleButtonClick}>
使用我的插件
</button>
{result && <p>结果: {result}</p>}
</div>
);
}Vue示例:
vue
<template>
<div>
<button @click="usePlugin">使用我的插件</button>
<p v-if="result">结果: {{ result }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { WebFMyPlugin } from '@openwebf/webf-my-plugin';
const result = ref(null);
const usePlugin = async () => {
try {
result.value = await WebFMyPlugin.myAsyncMethod('测试');
} catch (error) {
console.error('失败:', error);
}
};
</script>Option 2: Direct Module Invocation (Legacy)
选项2:直接模块调用(旧版)
If for some reason the npm package isn't available, you can call the module directly:
javascript
// Direct invocation (not type-safe)
const result = await webf.invokeModuleAsync(
'MyPlugin', // Module name (must match what you registered)
'myAsyncMethod', // Method name
'input' // Arguments
);
// With multiple arguments
const complexResult = await webf.invokeModuleAsync(
'MyPlugin',
'complexMethod',
{
someField: 'value',
timeout: 3000,
verbose: true
}
);
// Sync method invocation
const syncResult = webf.invokeModuleSync(
'MyPlugin',
'mySyncMethod',
'data'
);如果由于某些原因无法使用npm包,可以直接调用模块:
javascript
// 直接调用(非类型安全)
const result = await webf.invokeModuleAsync(
'MyPlugin', // 模块名称(必须与注册的名称一致)
'myAsyncMethod', // 方法名称
'输入' // 参数
);
// 带多个参数
const complexResult = await webf.invokeModuleAsync(
'MyPlugin',
'complexMethod',
{
someField: '值',
timeout: 3000,
verbose: true
}
);
// 同步方法调用
const syncResult = webf.invokeModuleSync(
'MyPlugin',
'mySyncMethod',
'数据'
);Feature Detection
功能检测
Always check if your plugin is available:
typescript
// Check if plugin exists
if (typeof WebFMyPlugin !== 'undefined') {
// Plugin is available
await WebFMyPlugin.myAsyncMethod('test');
} else {
// Plugin not registered or npm package not installed
console.warn('MyPlugin is not available');
// Provide fallback behavior
}始终检查插件是否可用:
typescript
// 检查插件是否存在
if (typeof WebFMyPlugin !== 'undefined') {
// 插件可用
await WebFMyPlugin.myAsyncMethod('测试');
} else {
// 插件未注册或npm包未安装
console.warn('MyPlugin不可用');
// 提供回退行为
}Error Handling
错误处理
typescript
async function safePluginCall() {
// Check availability
if (typeof WebFMyPlugin === 'undefined') {
throw new Error('MyPlugin is not installed');
}
try {
const result = await WebFMyPlugin.complexMethod({
someField: 'value'
});
// Check result status
if (result.success === 'true') {
return result.data;
} else {
throw new Error(result.error || 'Unknown error');
}
} catch (error) {
console.error('Plugin call failed:', error);
throw error;
}
}typescript
async function safePluginCall() {
// 检查可用性
if (typeof WebFMyPlugin === 'undefined') {
throw new Error('MyPlugin未安装');
}
try {
const result = await WebFMyPlugin.complexMethod({
someField: '值'
});
// 检查结果状态
if (result.success === 'true') {
return result.data;
} else {
throw new Error(result.error || '未知错误');
}
} catch (error) {
console.error('插件调用失败:', error);
throw error;
}
}Development Workflow
开发流程
During Plugin Development
插件开发期间
bash
undefinedbash
undefinedTerminal 1: Flutter app
终端1:Flutter应用
cd my-flutter-app
cd my-flutter-app
Use local path in pubspec.yaml
在pubspec.yaml中使用本地路径
flutter run
flutter run
Terminal 2: Web project
终端2:Web项目
cd my-web-project
cd my-web-project
Install local npm package
安装本地npm包
npm install ../webf-my-plugin-npm
npm run dev
undefinednpm install ../webf-my-plugin-npm
npm run dev
undefinedAfter Publishing
发布后
bash
undefinedbash
undefinedUpdate to published versions
更新为已发布版本
cd my-flutter-app
cd my-flutter-app
Update pubspec.yaml to use pub.dev version
在pubspec.yaml中更新为pub.dev版本
flutter pub get
flutter run
cd my-web-project
flutter pub get
flutter run
cd my-web-project
Install from npm
从npm安装
npm install @openwebf/webf-my-plugin
npm run dev
undefinednpm install @openwebf/webf-my-plugin
npm run dev
undefinedDistribution Options
分发选项
Public Distribution
公开发布
Flutter Package:
- Publish to pub.dev (free, public)
- Anyone can install with
webf_my_plugin: ^1.0.0
npm Package:
- Publish to npmjs.com (free, public)
- Anyone can install with
npm install @openwebf/webf-my-plugin
Flutter包:
- 发布到pub.dev(免费,公开)
- 任何人都可以使用安装
webf_my_plugin: ^1.0.0
npm包:
- 发布到npmjs.com(免费,公开)
- 任何人都可以使用安装
npm install @openwebf/webf-my-plugin
Private Distribution
私有发布
Flutter Package:
yaml
undefinedFlutter包:
yaml
undefinedInstall from private registry
从私有注册表安装
dependencies:
webf_my_plugin:
hosted:
name: webf_my_plugin
url: https://your-company-flutter-registry.com
version: ^1.0.0
dependencies:
webf_my_plugin:
hosted:
name: webf_my_plugin
url: https://your-company-flutter-registry.com
version: ^1.0.0
Or from Git repository
或从Git仓库安装
dependencies:
webf_my_plugin:
git:
url: https://github.com/yourcompany/webf_my_plugin.git
ref: v1.0.0
**npm Package:**
```bashdependencies:
webf_my_plugin:
git:
url: https://github.com/yourcompany/webf_my_plugin.git
ref: v1.0.0
**npm包:**
```bashInstall from private registry
从私有注册表安装
npm install @yourcompany/webf-my-plugin --registry=https://registry.company.com
npm install @yourcompany/webf-my-plugin --registry=https://registry.company.com
Or configure .npmrc
或配置.npmrc
echo "@yourcompany:registry=https://registry.company.com" >> .npmrc
npm install @yourcompany/webf-my-plugin
undefinedecho "@yourcompany:registry=https://registry.company.com" >> .npmrc
npm install @yourcompany/webf-my-plugin
undefinedLocal Development
本地开发
Flutter Package:
yaml
dependencies:
webf_my_plugin:
path: ../webf_my_plugin # Relative pathnpm Package:
bash
npm install ../webf-my-plugin-npmFlutter包:
yaml
dependencies:
webf_my_plugin:
path: ../webf_my_plugin # 相对路径npm包:
bash
npm install ../webf-my-plugin-npmOr use npm link
或使用npm link
cd ../webf-my-plugin-npm
npm link
cd my-web-project
npm link @openwebf/webf-my-plugin
undefinedcd ../webf-my-plugin-npm
npm link
cd my-web-project
npm link @openwebf/webf-my-plugin
undefinedComplete Installation Example
完整安装示例
Scenario: You've built a camera plugin and want to use it in your app.
1. Flutter side (main.dart):
dart
import 'package:webf/webf.dart';
import 'package:webf_camera/webf_camera.dart'; // Your plugin
void main() {
WebFControllerManager.instance.initialize(WebFControllerManagerConfig(
maxAliveInstances: 2,
maxAttachedInstances: 1,
));
// Register your camera plugin
WebF.defineModule((context) => CameraModule(context));
runApp(MyApp());
}2. JavaScript side (app.tsx):
typescript
import { WebFCamera } from '@openwebf/webf-camera';
function CameraApp() {
const [cameras, setCameras] = useState([]);
useEffect(() => {
async function loadCameras() {
// Check if plugin is available
if (typeof WebFCamera === 'undefined') {
console.error('Camera plugin not installed');
return;
}
try {
const result = await WebFCamera.getCameras();
if (result.success === 'true') {
setCameras(JSON.parse(result.cameras));
}
} catch (error) {
console.error('Failed to load cameras:', error);
}
}
loadCameras();
}, []);
const capturePhoto = async () => {
try {
const photoPath = await WebFCamera.capturePhoto(cameras[0].id);
console.log('Photo saved to:', photoPath);
} catch (error) {
console.error('Failed to capture:', error);
}
};
return (
<div>
<h1>Camera App</h1>
<button onClick={capturePhoto}>Capture Photo</button>
<ul>
{cameras.map(cam => (
<li key={cam.id}>{cam.name}</li>
))}
</ul>
</div>
);
}场景: 你构建了一个相机插件并想在应用中使用。
1. Flutter端(main.dart):
dart
import 'package:webf/webf.dart';
import 'package:webf_camera/webf_camera.dart'; // 你的插件
void main() {
WebFControllerManager.instance.initialize(WebFControllerManagerConfig(
maxAliveInstances: 2,
maxAttachedInstances: 1,
));
// 注册相机插件
WebF.defineModule((context) => CameraModule(context));
runApp(MyApp());
}2. JavaScript端(app.tsx):
typescript
import { WebFCamera } from '@openwebf/webf-camera';
function CameraApp() {
const [cameras, setCameras] = useState([]);
useEffect(() => {
async function loadCameras() {
// 检查插件是否可用
if (typeof WebFCamera === 'undefined') {
console.error('相机插件未安装');
return;
}
try {
const result = await WebFCamera.getCameras();
if (result.success === 'true') {
setCameras(JSON.parse(result.cameras));
}
} catch (error) {
console.error('加载相机失败:', error);
}
}
loadCameras();
}, []);
const capturePhoto = async () => {
try {
const photoPath = await WebFCamera.capturePhoto(cameras[0].id);
console.log('照片保存到:', photoPath);
} catch (error) {
console.error('拍照失败:', error);
}
};
return (
<div>
<h1>相机应用</h1>
<button onClick={capturePhoto}>拍照</button>
<ul>
{cameras.map(cam => (
<li key={cam.id}>{cam.name}</li>
))}
</ul>
</div>
);
}Common Plugin Patterns
常见插件模式
1. Wrapping Existing Flutter Packages
1. 封装现有Flutter包
Example: Wrapping a camera package
dart
import 'package:camera/camera.dart';
class CameraPluginModule extends CameraPluginModuleBindings {
CameraPluginModule(super.moduleManager);
List<CameraDescription>? _cameras;
Future<CameraListResult> getCameras() async {
try {
_cameras = await availableCameras();
final cameraList = _cameras!.map((cam) => {
'id': cam.name,
'name': cam.name,
'facing': cam.lensDirection.toString(),
}).toList();
return CameraListResult(
success: 'true',
cameras: jsonEncode(cameraList),
);
} catch (e) {
return CameraListResult(
success: 'false',
error: e.toString(),
);
}
}
Future<String> capturePhoto(String cameraId) async {
// Implementation for capturing photos
// Return file path or base64 data
}
}示例:封装相机包
dart
import 'package:camera/camera.dart';
class CameraPluginModule extends CameraPluginModuleBindings {
CameraPluginModule(super.moduleManager);
List<CameraDescription>? _cameras;
Future<CameraListResult> getCameras() async {
try {
_cameras = await availableCameras();
final cameraList = _cameras!.map((cam) => {
'id': cam.name,
'name': cam.name,
'facing': cam.lensDirection.toString(),
}).toList();
return CameraListResult(
success: 'true',
cameras: jsonEncode(cameraList),
);
} catch (e) {
return CameraListResult(
success: 'false',
error: e.toString(),
);
}
}
Future<String> capturePhoto(String cameraId) async {
// 拍照实现
// 返回文件路径或base64数据
}
}2. Handling Binary Data
2. 处理二进制数据
dart
Future<bool> processImageData(NativeByteData imageData) async {
try {
// Access raw bytes
final bytes = imageData.bytes;
// Process the data
await SomeFlutterPackage.processImage(bytes);
return true;
} catch (e) {
return false;
}
}TypeScript:
typescript
interface WebFMyPlugin {
processImageData(imageData: ArrayBuffer | Uint8Array): Promise<boolean>;
}dart
Future<bool> processImageData(NativeByteData imageData) async {
try {
// 访问原始字节
final bytes = imageData.bytes;
// 处理数据
await SomeFlutterPackage.processImage(bytes);
return true;
} catch (e) {
return false;
}
}TypeScript:
typescript
interface WebFMyPlugin {
processImageData(imageData: ArrayBuffer | Uint8Array): Promise<boolean>;
}3. Event Streams
3. 事件流
For continuous data streams (sensors, location updates):
dart
class SensorPluginModule extends SensorPluginModuleBindings {
StreamSubscription? _subscription;
Future<void> startListening(String sensorType) async {
_subscription = SensorPackage.stream.listen((data) {
// Send events to JavaScript
moduleManager.emitModuleEvent(
'SensorPlugin',
'data',
{'value': data.value, 'timestamp': data.timestamp.toString()},
);
});
}
Future<void> stopListening() async {
await _subscription?.cancel();
_subscription = null;
}
void dispose() {
_subscription?.cancel();
super.dispose();
}
}JavaScript:
typescript
// Listen for events
webf.on('SensorPlugin:data', (event) => {
console.log('Sensor data:', event.detail);
});
await WebFSensorPlugin.startListening('accelerometer');对于连续数据流(传感器、位置更新):
dart
class SensorPluginModule extends SensorPluginModuleBindings {
StreamSubscription? _subscription;
Future<void> startListening(String sensorType) async {
_subscription = SensorPackage.stream.listen((data) {
// 向JavaScript发送事件
moduleManager.emitModuleEvent(
'SensorPlugin',
'data',
{'value': data.value, 'timestamp': data.timestamp.toString()},
);
});
}
Future<void> stopListening() async {
await _subscription?.cancel();
_subscription = null;
}
void dispose() {
_subscription?.cancel();
super.dispose();
}
}JavaScript:
typescript
// 监听事件
webf.on('SensorPlugin:data', (event) => {
console.log('传感器数据:', event.detail);
});
await WebFSensorPlugin.startListening('accelerometer');4. Permission Handling
4. 权限处理
dart
import 'package:permission_handler/permission_handler.dart';
class PermissionPluginModule extends PermissionPluginModuleBindings {
Future<PermissionResult> requestPermission(String permissionType) async {
Permission permission;
switch (permissionType) {
case 'camera':
permission = Permission.camera;
break;
case 'microphone':
permission = Permission.microphone;
break;
default:
return PermissionResult(
granted: 'false',
message: 'Unknown permission type',
);
}
final status = await permission.request();
return PermissionResult(
granted: status.isGranted ? 'true' : 'false',
status: status.toString(),
message: _getPermissionMessage(status),
);
}
String _getPermissionMessage(PermissionStatus status) {
switch (status) {
case PermissionStatus.granted:
return 'Permission granted';
case PermissionStatus.denied:
return 'Permission denied';
case PermissionStatus.permanentlyDenied:
return 'Permission permanently denied. Please enable in settings.';
default:
return 'Unknown permission status';
}
}
}dart
import 'package:permission_handler/permission_handler.dart';
class PermissionPluginModule extends PermissionPluginModuleBindings {
Future<PermissionResult> requestPermission(String permissionType) async {
Permission permission;
switch (permissionType) {
case 'camera':
permission = Permission.camera;
break;
case 'microphone':
permission = Permission.microphone;
break;
default:
return PermissionResult(
granted: 'false',
message: '未知权限类型',
);
}
final status = await permission.request();
return PermissionResult(
granted: status.isGranted ? 'true' : 'false',
status: status.toString(),
message: _getPermissionMessage(status),
);
}
String _getPermissionMessage(PermissionStatus status) {
switch (status) {
case PermissionStatus.granted:
return '权限已授予';
case PermissionStatus.denied:
return '权限被拒绝';
case PermissionStatus.permanentlyDenied:
return '权限被永久拒绝,请在设置中启用。';
default:
return '未知权限状态';
}
}
}5. Platform-Specific Implementation
5. 平台特定实现
dart
import 'dart:io';
class PlatformPluginModule extends PlatformPluginModuleBindings {
Future<PlatformInfoResult> getPlatformInfo() async {
String platformName;
String platformVersion;
if (Platform.isAndroid) {
platformName = 'Android';
// Get Android version
} else if (Platform.isIOS) {
platformName = 'iOS';
// Get iOS version
} else if (Platform.isMacOS) {
platformName = 'macOS';
} else {
platformName = 'Unknown';
}
return PlatformInfoResult(
platform: platformName,
version: platformVersion,
isAndroid: Platform.isAndroid,
isIOS: Platform.isIOS,
);
}
}dart
import 'dart:io';
class PlatformPluginModule extends PlatformPluginModuleBindings {
Future<PlatformInfoResult> getPlatformInfo() async {
String platformName;
String platformVersion;
if (Platform.isAndroid) {
platformName = 'Android';
// 获取Android版本
} else if (Platform.isIOS) {
platformName = 'iOS';
// 获取iOS版本
} else if (Platform.isMacOS) {
platformName = 'macOS';
} else {
platformName = 'Unknown';
}
return PlatformInfoResult(
platform: platformName,
version: platformVersion,
isAndroid: Platform.isAndroid,
isIOS: Platform.isIOS,
);
}
}Advanced Patterns
高级模式
1. Error Handling and Validation
1. 错误处理和验证
dart
Future<OperationResult> performOperation(OperationOptions options) async {
// Validate input
if (options.value == null || options.value!.isEmpty) {
return OperationResult(
success: 'false',
error: 'InvalidInput',
message: 'Value cannot be empty',
);
}
if (options.timeout != null && options.timeout! < 0) {
return OperationResult(
success: 'false',
error: 'InvalidTimeout',
message: 'Timeout must be positive',
);
}
try {
// Perform operation with timeout
final result = await Future.timeout(
_doOperation(options.value!),
Duration(milliseconds: options.timeout ?? 5000),
onTimeout: () => throw TimeoutException('Operation timed out'),
);
return OperationResult(
success: 'true',
data: result,
message: 'Operation completed',
);
} on TimeoutException catch (e) {
return OperationResult(
success: 'false',
error: 'Timeout',
message: e.message ?? 'Operation timed out',
);
} catch (e) {
return OperationResult(
success: 'false',
error: 'UnknownError',
message: e.toString(),
);
}
}dart
Future<OperationResult> performOperation(OperationOptions options) async {
// 验证输入
if (options.value == null || options.value!.isEmpty) {
return OperationResult(
success: 'false',
error: 'InvalidInput',
message: '值不能为空',
);
}
if (options.timeout != null && options.timeout! < 0) {
return OperationResult(
success: 'false',
error: 'InvalidTimeout',
message: '超时时间必须为正数',
);
}
try {
// 带超时执行操作
final result = await Future.timeout(
_doOperation(options.value!),
Duration(milliseconds: options.timeout ?? 5000),
onTimeout: () => throw TimeoutException('操作超时'),
);
return OperationResult(
success: 'true',
data: result,
message: '操作完成',
);
} on TimeoutException catch (e) {
return OperationResult(
success: 'false',
error: 'Timeout',
message: e.message ?? '操作超时',
);
} catch (e) {
return OperationResult(
success: 'false',
error: 'UnknownError',
message: e.toString(),
);
}
}2. Resource Management
2. 资源管理
dart
class ResourcePluginModule extends ResourcePluginModuleBindings {
final Map<String, Resource> _activeResources = {};
Future<String> createResource(ResourceOptions options) async {
final id = DateTime.now().millisecondsSinceEpoch.toString();
final resource = Resource(options);
await resource.initialize();
_activeResources[id] = resource;
return id;
}
Future<void> releaseResource(String resourceId) async {
final resource = _activeResources.remove(resourceId);
await resource?.dispose();
}
void dispose() {
// Clean up all resources
for (final resource in _activeResources.values) {
resource.dispose();
}
_activeResources.clear();
super.dispose();
}
}dart
class ResourcePluginModule extends ResourcePluginModuleBindings {
final Map<String, Resource> _activeResources = {};
Future<String> createResource(ResourceOptions options) async {
final id = DateTime.now().millisecondsSinceEpoch.toString();
final resource = Resource(options);
await resource.initialize();
_activeResources[id] = resource;
return id;
}
Future<void> releaseResource(String resourceId) async {
final resource = _activeResources.remove(resourceId);
await resource?.dispose();
}
void dispose() {
// 清理所有资源
for (final resource in _activeResources.values) {
resource.dispose();
}
_activeResources.clear();
super.dispose();
}
}3. Batching Operations
3. 批量操作
dart
Future<BatchResult> batchProcess(String itemsJson) async {
final List<dynamic> items = jsonDecode(itemsJson);
final results = <String, dynamic>{};
final errors = <String, String>{};
await Future.wait(
items.asMap().entries.map((entry) async {
final index = entry.key;
final item = entry.value;
try {
final result = await _processItem(item);
results[index.toString()] = result;
} catch (e) {
errors[index.toString()] = e.toString();
}
}),
);
return BatchResult(
success: errors.isEmpty ? 'true' : 'false',
results: jsonEncode(results),
errors: errors.isEmpty ? null : jsonEncode(errors),
processedCount: results.length,
totalCount: items.length,
);
}dart
Future<BatchResult> batchProcess(String itemsJson) async {
final List<dynamic> items = jsonDecode(itemsJson);
final results = <String, dynamic>{};
final errors = <String, String>{};
await Future.wait(
items.asMap().entries.map((entry) async {
final index = entry.key;
final item = entry.value;
try {
final result = await _processItem(item);
results[index.toString()] = result;
} catch (e) {
errors[index.toString()] = e.toString();
}
}),
);
return BatchResult(
success: errors.isEmpty ? 'true' : 'false',
results: jsonEncode(results),
errors: errors.isEmpty ? null : jsonEncode(errors),
processedCount: results.length,
totalCount: items.length,
);
}CLI Command Reference
CLI命令参考
Basic Generation
基础生成
bash
undefinedbash
undefinedGenerate npm package for a module
为模块生成npm包
webf module-codegen output-dir
--flutter-package-src=./my_package
--module-name=MyModule
--flutter-package-src=./my_package
--module-name=MyModule
webf module-codegen output-dir \
--flutter-package-src=./my_package \
--module-name=MyModule
Specify custom package name
指定自定义包名称
webf module-codegen output-dir
--flutter-package-src=./my_package
--module-name=MyModule
--package-name=@mycompany/my-plugin
--flutter-package-src=./my_package
--module-name=MyModule
--package-name=@mycompany/my-plugin
undefinedwebf module-codegen output-dir \
--flutter-package-src=./my_package \
--module-name=MyModule \
--package-name=@mycompany/my-plugin
undefinedAuto-Publishing
自动发布
bash
undefinedbash
undefinedPublish to npm after generation
生成后发布到npm
webf module-codegen output-dir
--flutter-package-src=./my_package
--module-name=MyModule
--publish-to-npm
--flutter-package-src=./my_package
--module-name=MyModule
--publish-to-npm
webf module-codegen output-dir \
--flutter-package-src=./my_package \
--module-name=MyModule \
--publish-to-npm
Publish to custom registry
发布到自定义注册表
webf module-codegen output-dir
--flutter-package-src=./my_package
--module-name=MyModule
--publish-to-npm
--npm-registry=https://registry.company.com/
--flutter-package-src=./my_package
--module-name=MyModule
--publish-to-npm
--npm-registry=https://registry.company.com/
undefinedwebf module-codegen output-dir \
--flutter-package-src=./my_package \
--module-name=MyModule \
--publish-to-npm \
--npm-registry=https://registry.company.com/
undefinedBest Practices
最佳实践
1. Naming Conventions
1. 命名规范
- Flutter package: (e.g.,
webf_{feature},webf_share)webf_camera - Module class: (e.g.,
{Feature}Module,ShareModule)CameraModule - Module name: Same as class without "Module" (e.g., "Share", "Camera")
- npm package: or
@openwebf/webf-{feature}@yourscope/webf-{feature}
- Flutter包:(例如:
webf_{feature}、webf_share)webf_camera - Module类:(例如:
{Feature}Module、ShareModule)CameraModule - 模块名称:与类名相同,去掉“Module”(例如:“Share”、“Camera”)
- npm包:或
@openwebf/webf-{feature}@yourscope/webf-{feature}
2. Error Handling
2. 错误处理
dart
// Always return structured error information
return ResultType(
success: 'false',
error: 'ErrorCode', // Machine-readable error code
message: 'Human-readable error message',
);
// Never throw exceptions to JavaScript
// Catch and convert to result objectsdart
// 始终返回结构化错误信息
return ResultType(
success: 'false',
error: 'ErrorCode', // 机器可读的错误码
message: '人类可读的错误消息',
);
// 永远不要向JavaScript抛出异常
// 捕获并转换为结果对象3. Documentation
3. 文档
dart
/// Brief one-line description.
///
/// Detailed explanation of what this method does.
///
/// Parameters:
/// - [param1]: Description of first parameter
/// - [param2]: Description of second parameter
///
/// Returns a [ResultType] with:
/// - `success`: "true" on success, "false" on failure
/// - `data`: The actual result data
/// - `error`: Error message if failed
///
/// Throws:
/// - Never throws to JavaScript. Returns error in result object.
///
/// Example:
/// ```dart
/// final result = await module.myMethod('input');
/// if (result.success == 'true') {
/// print('Success: ${result.data}');
/// }
/// ```
Future<ResultType> myMethod(String param1, int? param2) async {
// Implementation
}dart
/// 简短的单行描述。
///
/// 此方法功能的详细说明。
///
/// 参数:
/// - [param1]: 第一个参数的描述
/// - [param2]: 第二个参数的描述
///
/// 返回带有以下内容的[ResultType]:
/// - `success`: 成功为"true",失败为"false"
/// - `data`: 实际结果数据
/// - `error`: 失败时的错误消息
///
/// 抛出:
/// - 永远不会向JavaScript抛出异常。在结果对象中返回错误。
///
/// 示例:
/// ```dart
/// final result = await module.myMethod('input');
/// if (result.success == 'true') {
/// print('成功: ${result.data}');
/// }
/// ```
Future<ResultType> myMethod(String param1, int? param2) async {
// 实现
}4. Type Safety
4. 类型安全
typescript
// Use interfaces for complex types
interface MyOptions {
value: string;
timeout?: number;
retries?: number;
}
// Use specific result types
interface MyResult {
success: string;
data?: any;
error?: string;
}
// Avoid 'any' when possible
// Use union types for enums
type Platform = 'ios' | 'android' | 'macos' | 'windows' | 'linux';typescript
// 为复杂类型使用接口
interface MyOptions {
value: string;
timeout?: number;
retries?: number;
}
// 使用特定的结果类型
interface MyResult {
success: string;
data?: any;
error?: string;
}
// 尽可能避免使用'any'
// 为枚举使用联合类型
type Platform = 'ios' | 'android' | 'macos' | 'windows' | 'linux';5. Testing
5. 测试
dart
// Create tests for your module
import 'package:flutter_test/flutter_test.dart';
import 'package:webf_my_plugin/webf_my_plugin.dart';
void main() {
group('MyPluginModule', () {
late MyPluginModule module;
setUp(() {
module = MyPluginModule(mockModuleManager);
});
tearDown(() {
module.dispose();
});
test('myAsyncMethod returns correct result', () async {
final result = await module.myAsyncMethod('test');
expect(result, 'Processed: test');
});
test('handles errors gracefully', () async {
final result = await module.complexMethod(MyOptionsType(
someField: 'invalid',
));
expect(result.success, 'false');
expect(result.error, isNotNull);
});
});
}dart
// 为模块创建测试
import 'package:flutter_test/flutter_test.dart';
import 'package:webf_my_plugin/webf_my_plugin.dart';
void main() {
group('MyPluginModule', () {
late MyPluginModule module;
setUp(() {
module = MyPluginModule(mockModuleManager);
});
tearDown(() {
module.dispose();
});
test('myAsyncMethod返回正确结果', () async {
final result = await module.myAsyncMethod('测试');
expect(result, '已处理: 测试');
});
test('优雅处理错误', () async {
final result = await module.complexMethod(MyOptionsType(
someField: '无效值',
));
expect(result.success, 'false');
expect(result.error, isNotNull);
});
});
}Troubleshooting
故障排除
Issue: Bindings file not found
问题:绑定文件未找到
Error:
Error: Could not find 'my_plugin_module_bindings_generated.dart'Solution:
- Make sure you've run the CLI code generation
- Check that file exists in the same directory
.module.d.ts - Verify the module interface is properly named ()
WebF{ModuleName} - Run again
webf module-codegen
错误:
Error: Could not find 'my_plugin_module_bindings_generated.dart'解决方案:
- 确保已运行CLI代码生成
- 检查同一目录下是否存在文件
.module.d.ts - 验证模块接口命名正确()
WebF{ModuleName} - 重新运行
webf module-codegen
Issue: Module not found in JavaScript
问题:JavaScript中找不到模块
Error:
Module 'MyPlugin' not foundSolution:
- Check that the Flutter package is in
pubspec.yaml - Verify the module is registered with in main.dart
WebF.defineModule() - Ensure module name matches exactly (case-sensitive)
- Run and rebuild the app
flutter pub get
错误:
Module 'MyPlugin' not found解决方案:
- 检查Flutter包是否在中
pubspec.yaml - 验证模块是否在main.dart中使用注册
WebF.defineModule() - 确保模块名称完全匹配(区分大小写)
- 运行并重新构建应用
flutter pub get
Issue: Method not working
问题:方法无法工作
Cause: Method name mismatch or incorrect parameters
Solution:
- Check method name matches between TypeScript and Dart
- Verify parameter types match
- Check async vs sync (Promise vs direct return)
- Look at console errors for details
原因: 方法名称不匹配或参数错误
解决方案:
- 检查TypeScript和Dart中的方法名称是否匹配
- 验证参数类型是否匹配
- 检查异步与同步(Promise vs 直接返回)
- 查看控制台错误获取详细信息
Issue: TypeScript types not working
问题:TypeScript类型无法工作
Cause: npm package not generated correctly
Solution:
bash
undefined原因: npm包生成不正确
解决方案:
bash
undefinedRegenerate with CLI
使用CLI重新生成
webf module-codegen output-dir
--flutter-package-src=./my_package
--module-name=MyModule
--flutter-package-src=./my_package
--module-name=MyModule
webf module-codegen output-dir \
--flutter-package-src=./my_package \
--module-name=MyModule
Check package.json exports
检查package.json导出
cd output-dir
cat package.json
cd output-dir
cat package.json
Should have "types": "./dist/index.d.ts"
应该包含"types": "./dist/index.d.ts"
undefinedundefinedReal-World Example: Share Plugin
真实示例:分享插件
See the complete implementation in the WebF repository at native_plugins/share for:
- Module implementation ()
share_module.dart - TypeScript definitions ()
share.module.d.ts - Error handling and platform-specific code
- Binary data handling
- Result types with detailed information
在WebF仓库的native_plugins/share中查看完整实现,包括:
- Module实现()
share_module.dart - TypeScript定义()
share.module.d.ts - 错误处理和平台特定代码
- 二进制数据处理
- 带有详细信息的结果类型
Resources
资源
- CLI Development Guide: cli/CLAUDE.md
- Module System Docs: webf/lib/src/module/
- Example Plugin: native_plugins/share
- WebF Architecture: docs/ARCHITECTURE.md
- Official Documentation: https://openwebf.com/en/docs/developer-guide/native-plugins
- CLI开发指南:cli/CLAUDE.md
- 模块系统文档:webf/lib/src/module/
- 示例插件:native_plugins/share
- WebF架构:docs/ARCHITECTURE.md
- 官方文档:https://openwebf.com/en/docs/developer-guide/native-plugins
Related Skills
相关技能
- Using Plugins: See skill for how to use existing plugins
webf-native-plugins - Native UI Development: See skill for creating UI components
webf-native-ui-dev - CLI Usage: See CLI documentation for code generation details
- 使用插件:查看技能了解如何使用现有插件
webf-native-plugins - 原生UI开发:查看技能了解如何创建UI组件
webf-native-ui-dev - CLI使用:查看CLI文档了解代码生成细节
Summary
总结
Creating Plugins
创建插件
- ✅ Native plugins expose Flutter/platform capabilities as JavaScript APIs
- ✅ Different from Native UI (functional vs visual)
- ✅ Write Dart Module class extending generated bindings
- ✅ Write TypeScript definitions (.d.ts) for each module
- ✅ Use WebF CLI () to generate npm packages and Dart bindings
webf module-codegen - ✅ Test in both Flutter and JavaScript environments
- ✅ Publish to pub.dev (Flutter) and npm (JavaScript)
- ✅ 原生插件将Flutter/平台能力暴露为JavaScript API
- ✅ 与原生UI不同(功能 vs 视觉)
- ✅ 编写继承自生成绑定类的Dart Module类
- ✅ 为每个模块编写TypeScript定义(.d.ts)
- ✅ 使用WebF CLI()生成npm包和Dart绑定
webf module-codegen - ✅ 在Flutter和JavaScript环境中测试
- ✅ 发布到pub.dev(Flutter)和npm(JavaScript)
Installing and Using Plugins
安装和使用插件
- ✅ Custom plugins are installed exactly like official plugins
- ✅ Requires TWO installations: Flutter package + npm package
- ✅ Add to and run
pubspec.yamlflutter pub get - ✅ Register with in Flutter app's main.dart
WebF.defineModule() - ✅ Install npm package:
npm install @openwebf/webf-my-plugin - ✅ Import and use in JavaScript:
import { WebFMyPlugin } from '@openwebf/webf-my-plugin' - ✅ Always check availability:
if (typeof WebFMyPlugin !== 'undefined') - ✅ Supports public (pub.dev/npm), private registries, and local paths
- ✅ 自定义插件的安装方式与官方插件完全相同
- ✅ 需要两次安装:Flutter包 + npm包
- ✅ 添加到并运行
pubspec.yamlflutter pub get - ✅ 在Flutter应用的main.dart中使用注册
WebF.defineModule() - ✅ 安装npm包:
npm install @openwebf/webf-my-plugin - ✅ 在JavaScript中导入并使用:
import { WebFMyPlugin } from '@openwebf/webf-my-plugin' - ✅ 始终检查可用性:
if (typeof WebFMyPlugin !== 'undefined') - ✅ 支持公开(pub.dev/npm)、私有注册表和本地路径
Best Practices
最佳实践
- ✅ Follow the Share plugin example at
native_plugins/share - ✅ Return structured results (never throw to JavaScript)
- ✅ Use TypeScript for type safety
- ✅ Handle errors gracefully with success/error flags
- ✅ Document thoroughly with JSDoc comments
- ✅ 参考中的分享插件示例
native_plugins/share - ✅ 返回结构化结果(永远不要向JavaScript抛出异常)
- ✅ 使用TypeScript确保类型安全
- ✅ 使用成功/错误标志优雅处理错误
- ✅ 使用JSDoc注释进行完整文档说明",