webf-native-plugin-dev

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

WebF 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的区别

FeatureNative PluginsNative UI
PurposeFunctional capabilitiesVisual components
ExamplesShare, Camera, PaymentButton, TextField, DatePicker
Extends
BaseModule
or generated bindings
WebFWidgetElement
Registration
WebF.defineModule()
WebFController.defineCustomElement()
Invocation
webf.invokeModuleAsync()
DOM APIs (properties, methods, events)
RenderingNo visual outputRenders Flutter widgets
Use CasePlatform features, data processingNative-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
用途功能能力视觉组件
示例分享、相机、支付按钮、输入框、日期选择器
继承类
BaseModule
或生成的绑定类
WebFWidgetElement
注册方式
WebF.defineModule()
WebFController.defineCustomElement()
调用方式
webf.invokeModuleAsync()
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()
    ,
    localStorage
    , Canvas 2D, etc.?
  • If YES → Use standard web APIs (no plugin needed)
Step 2: Check if an official plugin exists
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()
    localStorage
    、Canvas 2D等?
  • 如果是 → 使用标准Web API(无需插件)
步骤2:检查官方插件是否存在
步骤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
    webf-native-ui-dev
    skill instead)
  • ❌ Standard web APIs already provide the functionality
  • ❌ An official plugin already exists (use
    webf-native-plugins
    skill)
  • ❌ 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
undefined
bash
undefined

1. 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
undefined
webf module-codegen my-plugin-npm --flutter-package-src=./flutter_package
undefined

Step-by-Step Guide

分步指南

Step 1: Create Flutter Package Structure

步骤1:创建Flutter包结构

Create a standard Flutter package:
bash
undefined
创建标准Flutter包:
bash
undefined

Create 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.0
flutter 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.0

Step 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
.d.ts
file alongside your Dart file:
Example: 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
    Promise<T>
    for async methods
  • Define separate interfaces for complex types
  • Use
    string
    for success/failure flags (for backward compatibility)
在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
undefined

Install 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

**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
undefined
webf 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
undefined

Step 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-plugin
typescript
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-plugin
typescript
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
undefined
bash
undefined

In Flutter package directory

在Flutter包目录中

flutter pub publish
flutter pub publish

Or for private packages

对于私有包

flutter pub publish --server=https://your-private-registry.com
undefined
flutter pub publish --server=https://your-private-registry.com
undefined

Publish npm Package

发布npm包

bash
undefined
bash
undefined

Automatic publishing with CLI

使用CLI自动发布

webf module-codegen webf-my-plugin-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.yaml
:
yaml
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_plugin
Run:
bash
flutter pub get
在Flutter应用的
pubspec.yaml
中:
yaml
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 get

Step 2: Register the Plugin Module

步骤2:注册插件模块

In your Flutter app's
main.dart
:
dart
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.dart
中:
dart
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
undefined

Install 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
undefined
npm install ../webf-my-plugin-npm
undefined

Usage 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
undefined
bash
undefined

Terminal 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
undefined
npm install ../webf-my-plugin-npm npm run dev
undefined

After Publishing

发布后

bash
undefined
bash
undefined

Update 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
undefined
npm install @openwebf/webf-my-plugin npm run dev
undefined

Distribution 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
undefined
Flutter包:
yaml
undefined

Install 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:**
```bash
dependencies: webf_my_plugin: git: url: https://github.com/yourcompany/webf_my_plugin.git ref: v1.0.0

**npm包:**
```bash

Install 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
undefined
echo "@yourcompany:registry=https://registry.company.com" >> .npmrc npm install @yourcompany/webf-my-plugin
undefined

Local Development

本地开发

Flutter Package:
yaml
dependencies:
  webf_my_plugin:
    path: ../webf_my_plugin  # Relative path
npm Package:
bash
npm install ../webf-my-plugin-npm
Flutter包:
yaml
dependencies:
  webf_my_plugin:
    path: ../webf_my_plugin  # 相对路径
npm包:
bash
npm install ../webf-my-plugin-npm

Or use npm link

或使用npm link

cd ../webf-my-plugin-npm npm link cd my-web-project npm link @openwebf/webf-my-plugin
undefined
cd ../webf-my-plugin-npm npm link cd my-web-project npm link @openwebf/webf-my-plugin
undefined

Complete 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
undefined
bash
undefined

Generate npm package for a module

为模块生成npm包

webf module-codegen output-dir
--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
undefined
webf module-codegen output-dir \ --flutter-package-src=./my_package \ --module-name=MyModule \ --package-name=@mycompany/my-plugin
undefined

Auto-Publishing

自动发布

bash
undefined
bash
undefined

Publish to npm after generation

生成后发布到npm

webf module-codegen output-dir
--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/
undefined
webf module-codegen output-dir \ --flutter-package-src=./my_package \ --module-name=MyModule \ --publish-to-npm \ --npm-registry=https://registry.company.com/
undefined

Best Practices

最佳实践

1. Naming Conventions

1. 命名规范

  • Flutter package:
    webf_{feature}
    (e.g.,
    webf_share
    ,
    webf_camera
    )
  • Module class:
    {Feature}Module
    (e.g.,
    ShareModule
    ,
    CameraModule
    )
  • Module name: Same as class without "Module" (e.g., "Share", "Camera")
  • npm package:
    @openwebf/webf-{feature}
    or
    @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 objects
dart
// 始终返回结构化错误信息
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:
  1. Make sure you've run the CLI code generation
  2. Check that
    .module.d.ts
    file exists in the same directory
  3. Verify the module interface is properly named (
    WebF{ModuleName}
    )
  4. Run
    webf module-codegen
    again
错误:
Error: Could not find 'my_plugin_module_bindings_generated.dart'
解决方案:
  1. 确保已运行CLI代码生成
  2. 检查同一目录下是否存在
    .module.d.ts
    文件
  3. 验证模块接口命名正确(
    WebF{ModuleName}
  4. 重新运行
    webf module-codegen

Issue: Module not found in JavaScript

问题:JavaScript中找不到模块

Error:
Module 'MyPlugin' not found
Solution:
  1. Check that the Flutter package is in
    pubspec.yaml
  2. Verify the module is registered with
    WebF.defineModule()
    in main.dart
  3. Ensure module name matches exactly (case-sensitive)
  4. Run
    flutter pub get
    and rebuild the app
错误:
Module 'MyPlugin' not found
解决方案:
  1. 检查Flutter包是否在
    pubspec.yaml
  2. 验证模块是否在main.dart中使用
    WebF.defineModule()
    注册
  3. 确保模块名称完全匹配(区分大小写)
  4. 运行
    flutter pub get
    并重新构建应用

Issue: Method not working

问题:方法无法工作

Cause: Method name mismatch or incorrect parameters
Solution:
  1. Check method name matches between TypeScript and Dart
  2. Verify parameter types match
  3. Check async vs sync (Promise vs direct return)
  4. Look at console errors for details
原因: 方法名称不匹配或参数错误
解决方案:
  1. 检查TypeScript和Dart中的方法名称是否匹配
  2. 验证参数类型是否匹配
  3. 检查异步与同步(Promise vs 直接返回)
  4. 查看控制台错误获取详细信息

Issue: TypeScript types not working

问题:TypeScript类型无法工作

Cause: npm package not generated correctly
Solution:
bash
undefined
原因: npm包生成不正确
解决方案:
bash
undefined

Regenerate with CLI

使用CLI重新生成

webf module-codegen output-dir
--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"

undefined
undefined

Real-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

资源

Related Skills

相关技能

  • Using Plugins: See
    webf-native-plugins
    skill for how to use existing plugins
  • Native UI Development: See
    webf-native-ui-dev
    skill for creating UI components
  • CLI Usage: See CLI documentation for code generation details
  • 使用插件:查看
    webf-native-plugins
    技能了解如何使用现有插件
  • 原生UI开发:查看
    webf-native-ui-dev
    技能了解如何创建UI组件
  • 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 (
    webf module-codegen
    ) to generate npm packages and Dart bindings
  • ✅ 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(
    webf module-codegen
    )生成npm包和Dart绑定
  • ✅ 在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
    pubspec.yaml
    and run
    flutter pub get
  • ✅ Register with
    WebF.defineModule()
    in Flutter app's main.dart
  • ✅ 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.yaml
    并运行
    flutter 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注释进行完整文档说明",