Flutter嵌入式JavaScript引擎插件quickjs的使用

Flutter嵌入式JavaScript引擎插件quickjs的使用

简介

quickjs-dart 是一个基于 Dart 的 quickjs 绑定,使用最新的 ffi 工具 native_assets_cli。它允许在 Dart 应用中运行 JavaScript 代码,而无需使用 Flutter 的通道机制。quickjs-dart 支持所有平台(除 Web 外),集成的 quickjs 版本为 2024-02-14

你可以通过以下命令运行示例:

dart --enable-experiment=native-assets run example/example.dart

异步操作

所有与 quickjs 原生代码的交互都通过 dart:ffi 在一个独立的隔离区(isolate)中运行,这确保了 JavaScript 评估不会阻塞主线程,这也是与 flutter_js 的不同之处。

以下是异步执行 JavaScript 代码的示例:

import 'package:quickjs/quickjs.dart';

void _runAsync() async {
  // 创建引擎管理器
  final manager = await JsEngineManager.create();
  
  // 创建一个名为 'my-tag' 的引擎实例
  final engine = await manager.createEngine('my-tag');
  
  // 执行 JavaScript 代码并获取结果
  final result = await engine.eval('console.log("Hello~");');
  
  // 打印 JavaScript 控制台输出
  print(result.stdout); // "Hello~"
  
  // 释放引擎资源
  await engine.dispose();
  
  // 释放管理器资源
  await manager.dispose();
}

同步操作

quickjs-dart 也可以在主线程中同步执行 JavaScript 代码,只需使用不同的对象:

import 'package:quickjs/src/native_js_engine.dart';

void _runSync() {
  // 创建原生引擎管理器
  final manager = NativeEngineManager();
  
  // 创建一个名为 'my-tag' 的原生引擎实例
  final engine = NativeJsEngine(name: 'my-tag');
  
  // 执行 JavaScript 代码并获取结果
  final result = engine.eval('3-4');
  
  // 打印计算结果
  print(result.value); // "-1"
  
  // 释放引擎资源
  engine.dispose();
  
  // 释放管理器资源
  manager.dispose();
}

回调通知

如果你想从 JavaScript 代码中直接接收数据,需要在 Dart 侧注册一个回调函数:

import 'package:quickjs/quickjs.dart';

void _registerBridge(int n) async {
  // 创建引擎管理器
  final manager = await JsEngineManager.create();
  
  // 创建一个名为 'tag' 的引擎实例
  final engine = await manager.createEngine('tag');
  
  // 注册一个名为 '_onDataChanged' 的回调函数
  engine.registerBridge('_onDataChanged', (data) {
    print('notified from js: $data');
  });
  
  // 定义 JavaScript 代码
  final code = """
let obj = {
  update(v) {
    let s = JSON.stringify(v);
    console.log(`update: \${s}`);
    _ffiNotify("_onDataChanged", s);
    return s;
  }
};
obj.update({"key": $n});
  """;
  
  // 执行 JavaScript 代码并获取结果
  final result = await engine.eval(code);
  
  // 打印评估结果和控制台输出
  print("eval result: '${result.value}'");
  print("eval stdout: '${result.stdout?.trim()}'");
  
  // 释放引擎资源
  await engine.dispose();
  
  // 释放管理器资源
  await manager.dispose();
}

内置 JavaScript 函数

quickjs-dart 提供了一些内置的 JavaScript 函数:

  • console.log
  • setTimeout
  • _ffiNotify

这些函数可以直接在 JavaScript 代码中使用,例如 _ffiNotify 用于从 JavaScript 调用 Dart 侧的回调函数。

完整示例 Demo

以下是一个完整的示例,展示了如何在 Flutter 应用中使用 quickjs-dart

import 'package:flutter/material.dart';
import 'package:quickjs/quickjs.dart';
import 'package:quickjs/src/native_js_engine.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('QuickJS Demo')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: _runAsync,
                child: Text('Run Async JS'),
              ),
              ElevatedButton(
                onPressed: _runSync,
                child: Text('Run Sync JS'),
              ),
              ElevatedButton(
                onPressed: () => _registerBridge(5),
                child: Text('Register Bridge'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  void _runAsync() async {
    final manager = await JsEngineManager.create();
    final engine = await manager.createEngine('tag');
    final result = await engine.eval('console.log(3+4);');
    print(result.stdout?.trim());
    await engine.dispose();
    await manager.dispose();
  }

  void _runSync() {
    final manager = NativeEngineManager();
    final engine = NativeJsEngine(name: 'tag');
    final result = engine.eval('3-4');
    print(result.value);
    engine.dispose();
    manager.dispose();
  }

  void _registerBridge(int n) async {
    final manager = await JsEngineManager.create();
    final engine = await manager.createEngine('tag');
    engine.registerBridge('_onDataChanged', (data) {
      print('notified from js: $data');
    });
    final code = """
let obj = {
  update(v) {
    let s = JSON.stringify(v);
    console.log(`update: \${s}`);
    _ffiNotify("_onDataChanged", s);
    return s;
  }
};
obj.update({"key": $n});
  """;
    final result = await engine.eval(code);
    print("eval result: '${result.value}'");
    print("eval stdout: '${result.stdout?.trim()}'");
    await engine.dispose();
    await manager.dispose();
  }
}

更多关于Flutter嵌入式JavaScript引擎插件quickjs的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter嵌入式JavaScript引擎插件quickjs的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,使用QuickJS作为嵌入式JavaScript引擎插件,可以通过创建一个自定义的Flutter插件来实现。QuickJS是一个小型的、快速的JavaScript引擎,适用于嵌入式应用。以下是一个基本的代码案例,展示了如何在Flutter中集成和使用QuickJS。

步骤 1: 创建Flutter插件项目

首先,创建一个新的Flutter插件项目。你可以使用以下命令:

flutter create --org com.example --template=plugin quickjs_flutter_plugin

步骤 2: 添加QuickJS到原生代码

quickjs_flutter_plugin/iosquickjs_flutter_plugin/android目录下,分别添加QuickJS的依赖和代码。

iOS

  1. quickjs_flutter_plugin/ios/Podfile中添加QuickJS的依赖(如果QuickJS有CocoaPods支持的话;否则,你可能需要手动集成)。
platform :ios, '10.0'

target 'quickjs_flutter_plugin' do
  use_frameworks!
  pod 'QuickJS', '~> 1.0'  # 假设QuickJS有一个CocoaPods包

  # Flutter Pod
  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
  1. 创建一个Objective-C或Swift桥接文件,用于与QuickJS交互。
// QuickJSPlugin.m
#import <Flutter/Flutter.h>
#import <QuickJS/QuickJS.h>  // 假设QuickJS头文件是这样导入的

@interface QuickJSPlugin : NSObject<FlutterPlugin>
@end

@implementation QuickJSPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
    FlutterMethodChannel *channel = [FlutterMethodChannel
                                     methodChannelWithName:@"quickjs_flutter_plugin"
                                     binaryMessenger:registrar.messenger];
    
    QuickJSPlugin *instance = [[QuickJSPlugin alloc] init];
    [channel setMethodCallHandler:[instance methodCallHandler]];
}

- (FlutterMethodCallHandler)methodCallHandler {
    return ^(FlutterMethodCall *call, FlutterResult result) {
        if ([@"execute" isEqualToString:call.method]) {
            NSString *jsCode = call.arguments[@"code"];
            
            // 初始化QuickJS环境
            JSRuntime *rt = JS_NewRuntime();
            JSContext *ctx = JS_NewContext(rt);
            
            // 执行JavaScript代码
            JSValue resultVal = JS_Eval(ctx, [jsCode UTF8String], strlen([jsCode UTF8String]), "<input>", JS_EVAL_TYPE_GLOBAL);
            
            // 处理JavaScript执行结果
            if (JS_IsException(resultVal)) {
                result(@[@"Error executing JavaScript"]);
            } else {
                // 将结果转换为字符串并返回给Flutter
                char *str = JS_ToCString(ctx, resultVal);
                result(@[NSString stringWithUTF8String:str]);
                JS_FreeCString(ctx, str);
            }
            
            // 清理QuickJS环境
            JS_FreeContext(ctx);
            JS_FreeRuntime(rt);
        } else {
            result(FlutterMethodNotImplemented);
        }
    };
}

@end

Android

  1. quickjs_flutter_plugin/android/build.gradle中添加QuickJS的依赖(如果QuickJS有Gradle支持的话;否则,你可能需要手动集成)。
dependencies {
    implementation 'com.example:quickjs:1.0'  // 假设QuickJS有一个Gradle包
}
  1. 创建一个Java或Kotlin桥接文件,用于与QuickJS交互。
// QuickJSPlugin.java
package com.example.quickjs_flutter_plugin;

import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

public class QuickJSPlugin implements FlutterPlugin, MethodCallHandler, ActivityAware {
    private MethodChannel channel;

    @Override
    public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
        channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "quickjs_flutter_plugin");
        channel.setMethodCallHandler(this);
    }

    @Override
    public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
        if (call.method.equals("execute")) {
            String jsCode = (String) call.arguments;

            // 初始化QuickJS环境(这里需要根据QuickJS的API来实现)
            // 假设QuickJS有一个静态方法`execute`用于执行JavaScript代码
            String resultStr = QuickJSEngine.execute(jsCode);

            result.success(resultStr);
        } else {
            result.notImplemented();
        }
    }

    @Override
    public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
        channel.setMethodCallHandler(null);
    }

    @Override
    public void onAttachedToActivity(ActivityPluginBinding binding) {
        // No-op
    }

    @Override
    public void onDetachedFromActivityForConfigChanges() {
        // No-op
    }

    @Override
    public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {
        // No-op
    }

    @Override
    public void onDetachedFromActivity() {
        // No-op
    }
}

注意:由于QuickJS没有直接的Gradle包或CocoaPods包,这里的依赖添加是假设性的。你可能需要从QuickJS的官方仓库手动下载并集成其源码。

步骤 3: 在Flutter中使用插件

最后,在你的Flutter项目中使用这个插件。

import 'package:flutter/material.dart';
import 'package:quickjs_flutter_plugin/quickjs_flutter_plugin.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter QuickJS Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              String jsCode = 'console.log("Hello from Flutter!"); 42;';
              String result = await QuickJSPlugin().execute(jsCode);
              print('Result: $result');
            },
            child: Text('Execute JS'),
          ),
        ),
      ),
    );
  }
}

确保你已经在你的pubspec.yaml文件中添加了插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  quickjs_flutter_plugin:
    path: ../quickjs_flutter_plugin  # 指向你的插件项目路径

注意事项

  1. QuickJS的集成可能涉及到底层C代码的编译和链接,这在Flutter插件中可能比较复杂。
  2. 考虑到性能和安全性,确保你正确地管理QuickJS的生命周期和内存。
  3. 这个示例代码是基于假设的QuickJS API和集成方式,实际使用时你可能需要根据QuickJS的文档和API进行调整。

希望这个代码案例能帮助你在Flutter项目中集成和使用QuickJS!

回到顶部