Flutter动态执行Dart代码插件dart_eval的使用

发布于 1周前 作者 songsunli 来自 Flutter

Flutter动态执行Dart代码插件dart_eval的使用

简介

dart_eval 是一个用Dart编写的可扩展字节码编译器和解释器,它使Flutter和Dart AOT(Ahead Of Time)能够进行动态执行和代码推送。以下是关于dart_eval的基本介绍、使用方法以及一些高级特性。

基本信息

  • 构建状态Build status
  • 许可证License: BSD3
  • 在线示例Web example
  • GitHub StarStar on Github

包版本信息

Package Version
dart_eval
flutter_eval
eval_annotation

使用方法

基础使用示例

import 'package:dart_eval/dart_eval.dart';

void main() {
  print(eval('2 + 2')); // 输出 4
  
  final program = r'''
      class Cat {
        Cat(this.name);
        final String name;
        String speak() => "I'm $name!";
      }
      String main() {
        final cat = Cat('Fluffy');
        return cat.speak();
      }
  ''';
  
  print(eval(program, function: 'main')); // 输出 'I'm Fluffy!'
}

传递参数

在大多数情况下,应该将传递给dart_eval的参数包装在$Value中,例如$String$Map。对于intdoubleboolList类型,可以直接传递而不需包装。

final program = '''
  int main(int count, String str) {
    return count + str.length;
  }
''';

print(eval(program, function: 'main', args: [1, $String('Hi!')])); // 输出 4

传递回调函数

可以使用$Closure将回调函数作为参数传递给dart_eval

import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/dart_eval_bridge.dart';

void main() {
  final program = '''
    void main(Function callback) {
      callback('Hello');
    }
  ''';

  eval(program, function: 'main', args: [
    $Closure((runtime, target, args) {
      print(args[0]!.$value + '!'); // 输出 'Hello!'
      return null;
    })
  ]);
}

高级用法

可以通过直接使用Compiler和Runtime类来实现更高级的功能。

import 'package:dart_eval/dart_eval.dart';

void main() {
  final compiler = Compiler();
  
  final program = compiler.compile({'my_package': {
    'main.dart': '''
      import 'package:my_package/finder.dart';
      void main() {
        final parentheses = findParentheses('Hello (world)');
        if (parentheses.isNotEmpty) print(parentheses); 
      }
    ''',
    'finder.dart': r'''
      List<int> findParentheses(string) {
        final regex = RegExp(r'\((.*?)\)');
        final matches = regex.allMatches(string);
        return matches.map((match) => match.start).toList();
      }
    '''
  }});
  
  final runtime = Runtime.ofProgram(program);
  print(runtime.executeLib(
    'package:my_package/main.dart', 'main')); // 输出 '[6]'
}

编译到文件

推荐预先编译Dart代码为EVC字节码,以避免运行时编译开销。

import 'dart:io';
import 'package:dart_eval/dart_eval.dart';

void main() {
  final compiler = Compiler();
  
  final program = compiler.compile({'my_package': {
    'main.dart': '''
      int main() {
        var count = 0;
        for (var i = 0; i < 1000; i++) {
          count += i;
        }
        return count;
      }
    '''
  }});
  
  final bytecode = program.write();
  
  final file = File('program.evc');
  file.writeAsBytesSync(bytecode);
}

// 之后可以加载并执行程序
import 'dart:io';
import 'package:dart_eval/dart_eval.dart';

void main() {
  final file = File('program.evc');
  final bytecode = file.readAsBytesSync().buffer.asByteData();
  
  final runtime = Runtime(bytecode);
  print(runtime.executeLib(
    'package:my_package/main.dart', 'main')); // 输出 '499500'
}

CLI工具

dart_eval提供了CLI工具,允许编译现有Dart项目为EVC字节码,并运行和检查EVC文件。

安装CLI工具

dart pub global activate dart_eval

编译项目

cd my_project
dart_eval compile -o program.evc

运行程序

dart_eval run program.evc -p package:my_package/main.dart -f main

检查EVC文件

dart_eval dump program.evc

完整示例Demo

以下是一个完整的示例,展示了如何使用dart_eval动态执行Dart代码并与其他Dart代码进行交互。

import 'package:dart_eval/dart_eval.dart';
import 'package:dart_eval/dart_eval_bridge.dart';
import 'package:dart_eval/dart_eval_extensions.dart';
import 'package:dart_eval/stdlib/core.dart';

// ** 示例类定义 **
class TimestampedTime {
  const TimestampedTime(this.utcTime, {this.timezoneOffset = 0});

  final int utcTime;
  final int timezoneOffset;
}

abstract class WorldTimeTracker {
  WorldTimeTracker();

  TimestampedTime getTimeFor(String country);
}

// ** 主代码 **
void main(List<String> args) {
  final source = '''
    import 'package:example/bridge.dart';
    
    class MyWorldTimeTracker extends WorldTimeTracker {

      MyWorldTimeTracker();
      
      static TimestampedTime _currentTimeWithOffset(int offset) {
        return TimestampedTime(DateTime.now().millisecondsSinceEpoch,
          timezoneOffset: offset);
      }
      
      @override
      TimestampedTime getTimeFor(String country) {
        final countries = <String, TimestampedTime> {
          'USA': _currentTimeWithOffset(4),
          'UK': _currentTimeWithOffset(6),
        };
      
        return countries[country];
      }
    }
    
    MyWorldTimeTracker fn(String country) {
      final timeTracker = MyWorldTimeTracker();
      final myTime = timeTracker.getTimeFor(country);
      
      print(country + ' timezone offset: ' + myTime.timezoneOffset.toString() + ' (from Eval!)');
      
      return timeTracker;
    }
  ''';

  // 创建编译器并定义类的桥接声明
  final compiler = Compiler();
  compiler.defineBridgeClasses(
      [$TimestampedTime.$declaration, $WorldTimeTracker$bridge.$declaration]);

  // 编译源代码为包含元数据和字节码的Program
  final program = compiler.compile({
    'example': {'main.dart': source}
  });

  // 创建Runtime并注册桥接函数
  final runtime = Runtime.ofProgram(program)
    ..registerBridgeFunc('package:example/bridge.dart', 'TimestampedTime.',
        $TimestampedTime.$new)
    ..registerBridgeFunc('package:example/bridge.dart', 'WorldTimeTracker.',
        $WorldTimeTracker$bridge.$new,
        isBridge: true);

  // 调用函数并转换结果类型
  final timeTracker = runtime.executeLib(
    'package:example/main.dart',
    'fn',
    [$String('USA')],
  ) as WorldTimeTracker;

  // 使用返回的桥接类
  print('UK timezone offset: ${timeTracker.getTimeFor('UK').timezoneOffset}'
      ' (from outside Eval!)');
}

/// 创建TimestampedTime的包装器
class $TimestampedTime implements TimestampedTime, $Instance {
  $TimestampedTime.wrap(this.$value) : _superclass = $Object($value);

  static final $type = BridgeTypeSpec(
    'package:example/bridge.dart',
    'TimestampedTime',
  ).ref;

  static final $declaration = BridgeClassDef(
    BridgeClassType($type),
    constructors: {
      '': BridgeFunctionDef(returns: $type.annotate, params: [
        'utcTime'.param(CoreTypes.int.ref.annotate)
      ], namedParams: [
        'timezoneOffset'.paramOptional(CoreTypes.int.ref.annotate)
      ]).asConstructor
    },
    fields: {
      'utcTime': BridgeFieldDef(CoreTypes.int.ref.annotate),
      'timezoneOffset': BridgeFieldDef(CoreTypes.int.ref.annotate)
    },
    wrap: true,
  );

  static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
    return $TimestampedTime.wrap(TimestampedTime(
      args[0]!.$value,
      timezoneOffset: args[1]?.$value ?? 0,
    ));
  }

  @override
  final TimestampedTime $value;

  @override
  TimestampedTime get $reified => $value;

  final $Instance _superclass;

  @override
  $Value? $getProperty(Runtime runtime, String identifier) {
    switch (identifier) {
      case 'utcTime':
        return $int($value.utcTime);
      case 'timezoneOffset':
        return $int($value.timezoneOffset);
      default:
        return _superclass.$getProperty(runtime, identifier);
    }
  }

  @override
  int $getRuntimeType(Runtime runtime) => runtime.lookupType($type.spec!);

  @override
  void $setProperty(Runtime runtime, String identifier, $Value value) {
    return _superclass.$setProperty(runtime, identifier, value);
  }

  @override
  int get timezoneOffset => $value.timezoneOffset;

  @override
  int get utcTime => $value.utcTime;
}

/// 创建WorldTimeTracker的桥接类
class $WorldTimeTracker$bridge with $Bridge<WorldTimeTracker> implements WorldTimeTracker {
  static final $type = BridgeTypeSpec(
    'package:example/bridge.dart',
    'WorldTimeTracker',
  ).ref;

  static final $declaration = BridgeClassDef(
    BridgeClassType($type, isAbstract: true),
    constructors: {
      '': BridgeFunctionDef(returns: $type.annotate).asConstructor
    },
    methods: {
      'getTimeFor': BridgeFunctionDef(
        returns: $TimestampedTime.$type.annotate,
        params: ['country'.param(CoreTypes.string.ref.annotate)],
      ).asMethod
    },
    bridge: true,
  );

  static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
    return $WorldTimeTracker$bridge();
  }

  @override
  $Value? $bridgeGet(String identifier) {
    throw UnimplementedError(
      'Cannot get property "$identifier" on abstract class WorldTimeTracker',
    );
  }

  @override
  void $bridgeSet(String identifier, $Value value) {
    throw UnimplementedError(
      'Cannot set property "$identifier" on abstract class WorldTimeTracker',
    );
  }

  @override
  TimestampedTime getTimeFor(String country) => $_invoke(
        'getTimeFor',
        [$String(country)],
      );
}

通过以上内容,您可以了解dart_eval的基本使用方法及一些高级特性的应用。希望这些信息对您有所帮助!


更多关于Flutter动态执行Dart代码插件dart_eval的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter动态执行Dart代码插件dart_eval的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter项目中使用dart_eval插件来动态执行Dart代码的示例。dart_eval允许你在运行时解析和执行Dart代码字符串。

首先,确保你的Flutter项目已经配置好并且你已经在pubspec.yaml文件中添加了dart_eval依赖:

dependencies:
  flutter:
    sdk: flutter
  dart_eval: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来,让我们编写一个示例代码来展示如何使用dart_eval

示例代码

  1. 创建一个Flutter项目(如果还没有的话),并导航到lib目录。

  2. 编辑main.dart文件

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Dart Eval Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final TextEditingController _controller = TextEditingController();
  String _result = "";

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Dart Eval Example'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: <Widget>[
            TextField(
              controller: _controller,
              maxLines: 10,
              decoration: InputDecoration(
                hintText: 'Enter Dart code to evaluate',
              ),
            ),
            SizedBox(height: 16),
            ElevatedButton(
              onPressed: () async {
                setState(() {
                  _result = "Evaluating...";
                });

                try {
                  DartEvaluator evaluator = DartEvaluator();
                  String code = _controller.text;
                  String result = await evaluator.evaluate(code);
                  setState(() {
                    _result = result;
                  });
                } catch (e) {
                  setState(() {
                    _result = "Error: ${e.toString()}";
                  });
                }
              },
              child: Text('Evaluate'),
            ),
            SizedBox(height: 16),
            Text(_result),
          ],
        ),
      ),
    );
  }
}

代码解释

  1. 依赖引入

    • 引入flutterdart_eval包。
  2. UI结构

    • 使用MaterialAppScaffold来构建基本的Flutter应用结构。
    • 使用TextField来输入Dart代码。
    • 使用ElevatedButton来触发代码评估。
    • 使用Text来显示评估结果。
  3. 状态管理

    • 使用StatefulWidget_MyHomePageState来管理UI状态。
    • _controller用于管理TextField的文本输入。
    • _result用于存储评估结果。
  4. 评估Dart代码

    • 在按钮点击事件处理函数中,使用DartEvaluator来评估输入的Dart代码。
    • 捕获并处理任何可能抛出的异常。

注意事项

  • 确保dart_eval插件的最新版本兼容你的Flutter SDK版本。
  • dart_eval在运行时解析和执行代码,因此应谨慎使用以避免安全漏洞,特别是在处理不受信任的输入时。

这个示例展示了如何使用dart_eval在Flutter应用中动态执行Dart代码。你可以根据需要扩展这个示例,例如添加更多的输入验证或支持更复杂的Dart代码执行。

回到顶部