AOP for Flutter(Dart)

Related tags

Templates aspectd
Overview

AspectD

Awesome Flutter

Salute to AspectJ.

AspectD is an AOP(aspect oriented programming) framework for dart. Like other traditional aop framework, AspectD provides call&execute grammar. Besides, as we can't use dart:mirrors in flutter, AspectD also provides a way named inject enhancing the dart code manipulation.

Besides, AspectD provides a dill transformer container above which developers can implement their own transformers like hook, json, mirrors, etc.

Design

Aspectd Diagram

Suppose you have a flutter project named example located in hf_dir.

Installation

1. Create a dart package named aspectd_impl in hf_dir/example

flutter create --template=package aspectd_impl

2. Add aspectd&example dependency to aspectd_impl package

dependencies:
  flutter:
    sdk: flutter
  aspectd:
    git:
      url: git@github.com:XianyuTech/aspectd.git
      ref: stable/v2.2.3
  example:
    path: ../example

Remember to change the branch matching your flutter environment (stable supported currently). Fetch package dependency in aspectd_impl package

flutter packages get

3. Modify aspectd_impl package

aspectd_impl.dart(entrypoint)

import 'package:example/main.dart' as app;
import 'aop_impl.dart';

void main()=> app.main();

aop_impl.dart(aop implementation)

import 'package:aspectd/aspectd.dart';

@Aspect()
@pragma("vm:entry-point")
class ExecuteDemo {
  @pragma("vm:entry-point")
  ExecuteDemo();

  @Execute("package:example/main.dart", "_MyHomePageState", "-_incrementCounter")
  @pragma("vm:entry-point")
  void _incrementCounter(PointCut pointcut) {
    pointcut.proceed();
    print('KWLM called!');
  }
}

4. Patch flutter_tools to apply aspectd.dart.snapshot

cd path-for-flutter-git-repo
git apply --3way path-for-aspectd-package/0001-aspectd.patch
rm bin/cache/flutter_tools.stamp

On Windows, use "git am --reject --whitespace=fix aspectd\0001-aspectd.patch" to apply the patch instead.

As flutter_tools doesn't support hooks now, the aspectd.patch is necessary currently. As flutter is evolving, this patch might fail sometimes. However, It would be simple to resolve the conflicts as AspectD only adds two hooks when building dill. See https://github.com/alibaba-flutter/aspectd/issues/5 for more.

If you want to customize the aspectd_impl package, edit aspectdImplPackageRelPath(aspectd_impl package relative path to the example's pubspec.yaml) and aspectdImplPackageName(aspectd_impl package folder name and main entry file name) defined in path-for-flutter-git-repo/flutter/packages/flutter_tools/lib/src/aspectd.dart.

const String aspectdImplPackageRelPath = '..';
const String aspectdImplPackageName = 'aspectd_impl';

Step 1~3 are expected to run each time you want to add aspectd_impl to a flutter(dart) package. 4 is expected to run only once unless the dart-sdk changes. For example, If you upgrade flutter, you need to check if to re-run 4.

If you're using example with an aspectd_impl package not generated locally, remember to run flutter packages get in aspectd_impl package to get aspectd and check 4.

If the flutter version you want is not supported yet, see UPGRADE.md for more.

Tutorial

Now AspectD provides three ways to do AOP programming.

call

Every callsites for a specific function would be manipulated.

import 'package:aspectd/aspectd.dart';

@Aspect()
@pragma("vm:entry-point")
class CallDemo{
  @Call("package:app/calculator.dart","Calculator","-getCurTime")
  @pragma("vm:entry-point")
  Future<String> getCurTime(PointCut pointcut) async{
    print('Aspectd:KWLM02');
    print('${pointcut.sourceInfos.toString()}');
    Future<String> result = pointcut.proceed();
    String test = await result;
    print('Aspectd:KWLM03');
    print('${test}');
    return result;
  }

  @Call("package:app/calculator.dart","Calculator","+getCurTemporature")
  @pragma("vm:entry-point")
  String getCurTemporature(PointCut pointcut) {
    print('Aspectd:KWLM04');
    print('${pointcut.sourceInfos.toString()}');
    try{
      String res = pointcut.proceed();
    } catch (error, trace){
      print('Aspectd:KWLM05');
    }
    return null;
  }

  @Call("package:flutter/src/widgets/binding.dart","","+runApp")
  @pragma("vm:entry-point")
  static void runAppKWLM(PointCut pointcut){
    print('Aspectd:KWLM07');
    print('${pointcut.sourceInfos.toString()}');
    pointcut.proceed();
  }
}

In this case, notice that @Aspect() is needed to mark a class so that the aspectd will know that this class contains AspectD annotation informations. @pragma("vm:entry-point") is needed so that the class/function will not be removed by tree-shaking. For @Call("package:app/calculator.dart","Calculator","-getCurTime"), there are several things to know. Now call/execute/inject accept three positional parameters, package name, class name(If the procedure is a library method, this part is empty string), and function name. The function name may have a prefix('-' or '+'), '-' refers to instance method while '+' refers to library static method(like main) and class method. There is also a named parameter lineNum for inject so that AspectD know which line to inject a code snippet. The lineNum parameter is 1 based and code snippet would be injected before that line.

Besides, when you want to manipulate a static method(including library method and class method), your aop method(runAppKWLM here) should also be declared static. This requirement also applies when using execute command.

execute

Every implementation for a specific function would be manipulated.

import 'package:aspectd/aspectd.dart';

@Aspect()
@pragma("vm:entry-point")
class ExecuteDemo{
  @Execute("package:app/calculator.dart","Calculator","-getCurTime")
  @pragma("vm:entry-point")
  Future<String> getCurTime(PointCut pointcut) async{
    print('Aspectd:KWLM12');
    print('${pointcut.sourceInfos.toString()}');
    Future<String> result = pointcut.proceed();
    String test = await result;
    print('Aspectd:KWLM13');
    print('${test}');
    return result;
  }

  @Execute("package:app/calculator.dart","Calculator","+getCurTemporature")
  @pragma("vm:entry-point")
  String getCurTemporature(PointCut pointcut) {
    print('Aspectd:KWLM14');
    print('${pointcut.sourceInfos.toString()}');
    try{
      String res = pointcut.proceed();
    } catch (error, trace){
      print('Aspectd:KWLM15');
    }
    return null;
  }

  @Execute("package:flutter/src/widgets/binding.dart","","+runApp")
  @pragma("vm:entry-point")
  static void runAppKWLM(PointCut pointcut){
    print('Aspectd:KWLM17');
    print('${pointcut.sourceInfos.toString()}');
    pointcut.proceed();
  }
}

inject

For a original function like below:(package:flutter/src/widgets/gesture_detector.dart)

  @override
  Widget build(BuildContext context) {
    final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};

    if (onTapDown != null || onTapUp != null || onTap != null || onTapCancel != null) {
      gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
        () => TapGestureRecognizer(debugOwner: this),
        (TapGestureRecognizer instance) {
          instance
            ..onTapDown = onTapDown
            ..onTapUp = onTapUp
            ..onTap = onTap
            ..onTapCancel = onTapCancel;
        },
      );
    }
...
}
import 'package:aspectd/aspectd.dart';
import 'package:flutter/services.dart';

@Aspect()
@pragma("vm:entry-point")
class InjectDemo{
  @Inject("package:flutter/src/widgets/gesture_detector.dart","GestureDetector","-build", lineNum:452)
  @pragma("vm:entry-point")
  static void onTapBuild() {
    Object instance; //Aspectd Ignore
    Object context; //Aspectd Ignore
    print(instance);
    print(context);
    print('Aspectd:KWLM25');
  }
}

After that, the original build function will look like below:

  @override
  Widget build(BuildContext context) {
    final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};

    if (onTapDown != null || onTapUp != null || onTap != null || onTapCancel != null) {
      gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
        () => TapGestureRecognizer(debugOwner: this),
        (TapGestureRecognizer instance) {
          instance
            ..onTapDown = onTapDown
            ..onTapUp = onTapUp
            ..onTap = onTap
            ..onTapCancel = onTapCancel;
        },
    	print(instance);
    	print(context);
    	print('Aspectd:KWLM25');
      );
    }
...
}

Notice that //Aspectd Ignore part when using injection, we need to compile the aop package successfully so we need to declare the instance/context variable. However, when injecting to origin function (build in this case), variable declaration

Object instance; //Aspectd Ignore 
Object context; //Aspectd Ignore

would be discarded to avoid overring the original one.

Debug

Read DEBUG.md for more.

Compatibility

Stable version >= 1.0, currently v2.2.2

Notice

Because of the dart compilation implementation, there are several points to pay attention to:

  1. package:aspectd_impl/aspectd_impl.dart should contains the main entry for aspectd_impl package and contains a app.main call.
  2. Every aop implementation file should be imported by aspectd_impl.dart so that it will work in debug mode.
  3. @pragma("vm:entry-point") is needed to mark class/function to avoid been trimmed by tree-shaking.
  4. inject might fail in some cases while call&execute are expected to be more stable.
  5. If you want to disable AspectD, remove the aspectd.dart.snapshot located in aspectd or change the name of aspectd_impl package, or remove the @Aspect() annotation. Anyone will be fine.
  6. If you want to hook an instance method, the hook class should declare a default constructor and mark it with @pragma("vm:entry-point").

Contact

If you meet any problem when using AspectD, file a issue or contact me directly.

Contact Author

Comments
  • kernel 0.3.29 from git git@github.com:alibaba-flutter/sdk.git at eb9d9e in pkg/kernel

    kernel 0.3.29 from git [email protected]:alibaba-flutter/sdk.git at eb9d9e in pkg/kernel

    您好 请问我在aspectd 目录下 flutter packages get 之后 出现这个错误

    Target kernel_snapshot failed: FileSystemException: Cannot open file, path = '/Users/quyanhui/Documents/aspectd/aspectd_impl/.packages' (OS Error: No such file or directory, errno = 2) build failed. Command PhaseScriptExecution failed with a nonzero exit code note: Using new build system note: Building targets in parallel note: Planning build note: Constructing build description

    opened by robin0822 7
  •  flutter build apk 出错 运行example时没效果

    flutter build apk 出错 运行example时没效果

    Git error. Command: git fetch
    fatal: not a git repository (or any of the parent directories): .git
    Git error. Command: git fetch
    fatal: not a git repository (or any of the parent directories): .git
    Git error. Command: git fetch
    fatal: not a git repository (or any of the parent directories): .git
    Git error. Command: git fetch
    fatal: not a git repository (or any of the parent directories): .git

    opened by ckdgit 7
  • 跑了example工程中的例子 没有效果

    跑了example工程中的例子 没有效果

    [✓] Flutter (Channel master, v1.8.5-pre.77, on Mac OS X 10.14 18A391, locale zh-Hans-CN) • Flutter version 1.8.5-pre.77 at /Users/hb/development/flutter • Framework revision 8e5fb8c35d (3 hours ago), 2019-08-07 22:29:24 -0400 • Engine revision f200ee13aa • Dart version 2.5.0 (build 2.5.0-dev.1.0 f29f41f1a5)

    flutter git 目录执行过 git apply --3way path-for-aspectd-package/0001-aspectd.patch rm bin/cache/flutter_tools.stamp

    操作

    opened by Knight-ZXW 5
  • Inject 如何使用?

    Inject 如何使用?

    Hi All,

    我想在如下的一个方法中插入一段代码,如代码中注释所示

    void _handleTabControllerTick() {
        if (_controller.index != _currentIndex) {
          _currentIndex = _controller.index;
         //想要在这个位置插入一段代码
          if (widget.isScrollable)
            _scrollToCurrentIndex();
        }
        setState(() {
          // Rebuild the tabs after a (potentially animated) index change
          // has completed.
        });
      }
    

    因为这里的 _controller 和 _currentIndex 是 private 的,所以我想使用 Inject,在使用的时候发现必须要设置 lineNum,否则就会出错,因为 hook 的代码在未来可能会发生变化,现在有如下问题:

    1. 不使用 lineNum 是否可行?
    2. 有没有其他方式来做这个功能?
    3. 如何能拿到 _controller 和 _currentIndex,因为如果能拿到这两个值,我就可以判断是否需要进行 hook
    opened by GvcZhang 4
  • 配置协助~

    配置协助~

    我把库下载到了本地 但是跑不起来

    image

    想问一下pubspec.yaml里面的以来地址可以改成 https://github.com/alibaba-flutter/sdk.git 吗

    不知道为啥 不管用https还是ssh都好慢 flutter pub get 一直卡住....... 有没有什么好办法

    image

    opened by ConnyYue 4
  • 类的构造函数中包含widget参数,编译报错,求大佬帮忙看下

    类的构造函数中包含widget参数,编译报错,求大佬帮忙看下

    发现只要有类的无名构造函数中含有widget就无法编译通过。比如下面D类,在调用的时候有给widget传值,编译就会报错,把widget赋值给去掉,就可以正常插入代码,并成功打印。

    截屏2020-12-25 上午12 19 45 截屏2020-12-25 上午12 21 37

    Unhandled exception: Crash when compiling null, at character offset null: NoSuchMethodError: The getter 'line' was called on null. Receiver: null Tried calling: line #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5) #1 _WidgetCallSiteTransformer._constructLocation (package:kernel/transformations/track_widget_constructor_locations.dart:164:59) #2 _WidgetCallSiteTransformer._computeLocation (package:kernel/transformations/track_widget_constructor_locations.dart:272:30) #3 _WidgetCallSiteTransformer._addLocationArgument (package:kernel/transformations/track_widget_constructor_locations.dart:223:7) #4 _WidgetCallSiteTransformer.visitConstructorInvocation (package:kernel/transformations/track_widget_constructor_locations.dart:238:5) #5 ConstructorInvocation.accept (package:kernel/ast.dart:4204:44) #6 MapEntry.transformChildren (package:kernel/ast.dart:5504:21) #7 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #8 TreeVisitor.visitMapEntry (package:kernel/visitor.dart:265:37) #9 MapEntry.accept (package:kernel/ast.dart:5491:38) #10 transformList (package:kernel/ast.dart:9699:27) #11 MapLiteral.transformChildren (package:kernel/ast.dart:5454:5) #12 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #13 TreeVisitor.defaultExpression (package:kernel/visitor.dart:144:43) #14 TreeVisitor.visitMapLiteral (package:kernel/visitor.dart:186:41) #15 MapLiteral.accept (package:kernel/ast.dart:5441:44) #16 transformList (package:kernel/ast.dart:9699:27) #17 Arguments.transformChildren (package:kernel/ast.dart:3813:5) #18 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #19 TreeVisitor.visitArguments (package:kernel/visitor.dart:261:39) #20 Arguments.accept (package:kernel/ast.dart:3803:38) #21 ConstructorInvocation.transformChildren (package:kernel/ast.dart:4215:29) #22 _WidgetCallSiteTransformer.visitConstructorInvocation (package:kernel/transformations/track_widget_constructor_locations.dart:230:10) #23 ConstructorInvocation.accept (package:kernel/ast.dart:4204:44) #24 transformList (package:kernel/ast.dart:9699:27) #25 Arguments.transformChildren (package:kernel/ast.dart:3813:5) #26 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #27 TreeVisitor.visitArguments (package:kernel/visitor.dart:261:39) #28 Arguments.accept (package:kernel/ast.dart:3803:38) #29 StaticInvocation.transformChildren (package:kernel/ast.dart:4149:29) #30 _WidgetCallSiteTransformer.visitStaticInvocation (package:kernel/transformations/track_widget_constructor_locations.dart:202:10) #31 StaticInvocation.accept (package:kernel/ast.dart:4138:44) #32 VariableDeclaration.transformChildren (package:kernel/ast.dart:7001:33) #33 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #34 TreeVisitor.defaultStatement (package:kernel/visitor.dart:203:41) #35 TreeVisitor.visitVariableDeclaration (package:kernel/visitor.dart:225:7) #36 VariableDeclaration.accept (package:kernel/ast.dart:6987:43) #37 transformList (package:kernel/ast.dart:9699:27) #38 Block.transformChildren (package:kernel/ast.dart:5881:5) #39 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #40 TreeVisitor.defaultStatement (package:kernel/visitor.dart:203:41) #41 TreeVisitor.visitBlock (package:kernel/visitor.dart:206:31) #42 Block.accept (package:kernel/ast.dart:5873:43) #43 FunctionNode.transformChildren (package:kernel/ast.dart:2948:19) #44 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #45 TreeVisitor.visitFunctionNode (package:kernel/visitor.dart:260:45) #46 FunctionNode.accept (package:kernel/ast.dart:2932:38) #47 Procedure.transformChildren (package:kernel/ast.dart:2436:27) #48 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #49 _WidgetCallSiteTransformer.visitProcedure (package:kernel/transformations/track_widget_constructor_locations.dart:193:12) #50 Procedure.accept (package:kernel/ast.dart:2423:40) #51 transformList (package:kernel/ast.dart:9699:27) #52 Library.transformChildren (package:kernel/ast.dart:627:5) #53 WidgetCreatorTracker.transform (package:kernel/transformations/track_widget_constructor_locations.dart:504:15) #54 FlutterTarget.performPreConstantEvaluationTransformations (package:vm/target/flutter.dart:82:22) #55 KernelTarget.runBuildTransformations (package:front_end/src/fasta/kernel/kernel_target.dart:1093:19) #56 KernelTarget.buildComponent. (package:front_end/src/fasta/kernel/kernel_target.dart:390:7) #57 KernelTarget.buildComponent. (package:front_end/src/fasta/kernel/kernel_target.dart) #58 withCrashReporting (package:front_end/src/fasta/crash.dart:122:24) #59 KernelTarget.buildComponent (package:front_end/src/fasta/kernel/kernel_target.dart:380:12) #60 generateKernelInternal. (package:front_end/src/kernel_generator_impl.dart:177:38) #61 generateKernelInternal. (package:front_end/src/kernel_generator_impl.dart) #62 withCrashReporting (package:front_end/src/fasta/crash.dart:122:24) #63 generateKernelInternal (package:front_end/src/kernel_generator_impl.dart:73:10) #64 kernelForProgramInternal. (package:front_end/src/api_prototype/kernel_generator.dart:61:35) #65 CompilerContext.runWithOptions. (package:front_end/src/fasta/compiler_context.dart:135:20) #66 CompilerContext.runWithOptions. (package:front_end/src/fasta/compiler_context.dart) #67 CompilerContext.runInContext.. (package:front_end/src/fasta/compiler_context.dart:123:46) #68 new Future.sync (dart:async/future.dart:223:31) #69 CompilerContext.runInContext. (package:front_end/src/fasta/compiler_context.dart:123:19) #70 _rootRun (dart:async/zone.dart:1190:13) #71 _CustomZone.run (dart:async/zone.dart:1093:19) #72 _runZoned (dart:async/zone.dart:1630:10) #73 runZoned (dart:async/zone.dart:1550:10) #74 CompilerContext.runInContext (package:front_end/src/fasta/compiler_context.dart:122:12) #75 CompilerContext.runWithOptions (package:front_end/src/fasta/compiler_context.dart:133:10) #76 kernelForProgramInternal (package:front_end/src/api_prototype/kernel_generator.dart:60:32) #77 kernelForProgram (package:front_end/src/api_prototype/kernel_generator.dart:52:17) #78 compileToKernel (package:vm/kernel_front_end.dart:381:28) #79 FrontendCompiler.compile. (package:frontend_server/frontend_server.dart:542:54) #80 new Future. (dart:async/future.dart:175:37) #81 _rootRun (dart:async/zone.dart:1182:47) #82 _CustomZone.run (dart:async/zone.dart:1093:19) #83 _CustomZone.runGuarded (dart:async/zone.dart:997:7) #84 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1037:23) #85 _rootRun (dart:async/zone.dart:1190:13) #86 _CustomZone.run (dart:async/zone.dart:1093:19) #87 _CustomZone.bindCallback. (dart:async/zone.dart:1021:23) #88 Timer._createTimer. (dart:async-patch/timer_patch.dart:18:15) #89 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:397:19) #90 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:428:5) #91 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

    #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5) #1 _WidgetCallSiteTransformer._constructLocation (package:kernel/transformations/track_widget_constructor_locations.dart:164:59) #2 _WidgetCallSiteTransformer._computeLocation (package:kernel/transformations/track_widget_constructor_locations.dart:272:30) #3 _WidgetCallSiteTransformer._addLocationArgument (package:kernel/transformations/track_widget_constructor_locations.dart:223:7) #4 _WidgetCallSiteTransformer.visitConstructorInvocation (package:kernel/transformations/track_widget_constructor_locations.dart:238:5) #5 ConstructorInvocation.accept (package:kernel/ast.dart:4204:44) #6 MapEntry.transformChildren (package:kernel/ast.dart:5504:21) #7 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #8 TreeVisitor.visitMapEntry (package:kernel/visitor.dart:265:37) #9 MapEntry.accept (package:kernel/ast.dart:5491:38) #10 transformList (package:kernel/ast.dart:9699:27) #11 MapLiteral.transformChildren (package:kernel/ast.dart:5454:5) #12 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #13 TreeVisitor.defaultExpression (package:kernel/visitor.dart:144:43) #14 TreeVisitor.visitMapLiteral (package:kernel/visitor.dart:186:41) #15 MapLiteral.accept (package:kernel/ast.dart:5441:44) #16 transformList (package:kernel/ast.dart:9699:27) #17 Arguments.transformChildren (package:kernel/ast.dart:3813:5) #18 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #19 TreeVisitor.visitArguments (package:kernel/visitor.dart:261:39) #20 Arguments.accept (package:kernel/ast.dart:3803:38) #21 ConstructorInvocation.transformChildren (package:kernel/ast.dart:4215:29) #22 _WidgetCallSiteTransformer.visitConstructorInvocation (package:kernel/transformations/track_widget_constructor_locations.dart:230:10) #23 ConstructorInvocation.accept (package:kernel/ast.dart:4204:44) #24 transformList (package:kernel/ast.dart:9699:27) #25 Arguments.transformChildren (package:kernel/ast.dart:3813:5) #26 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #27 TreeVisitor.visitArguments (package:kernel/visitor.dart:261:39) #28 Arguments.accept (package:kernel/ast.dart:3803:38) #29 StaticInvocation.transformChildren (package:kernel/ast.dart:4149:29) #30 _WidgetCallSiteTransformer.visitStaticInvocation (package:kernel/transformations/track_widget_constructor_locations.dart:202:10) #31 StaticInvocation.accept (package:kernel/ast.dart:4138:44) #32 VariableDeclaration.transformChildren (package:kernel/ast.dart:7001:33) #33 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #34 TreeVisitor.defaultStatement (package:kernel/visitor.dart:203:41) #35 TreeVisitor.visitVariableDeclaration (package:kernel/visitor.dart:225:7) #36 VariableDeclaration.accept (package:kernel/ast.dart:6987:43) #37 transformList (package:kernel/ast.dart:9699:27) #38 Block.transformChildren (package:kernel/ast.dart:5881:5) #39 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #40 TreeVisitor.defaultStatement (package:kernel/visitor.dart:203:41) #41 TreeVisitor.visitBlock (package:kernel/visitor.dart:206:31) #42 Block.accept (package:kernel/ast.dart:5873:43) #43 FunctionNode.transformChildren (package:kernel/ast.dart:2948:19) #44 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #45 TreeVisitor.visitFunctionNode (package:kernel/visitor.dart:260:45) #46 FunctionNode.accept (package:kernel/ast.dart:2932:38) #47 Procedure.transformChildren (package:kernel/ast.dart:2436:27) #48 Transformer.defaultTreeNode (package:kernel/visitor.dart:653:10) #49 _WidgetCallSiteTransformer.visitProcedure (package:kernel/transformations/track_widget_constructor_locations.dart:193:12) #50 Procedure.accept (package:kernel/ast.dart:2423:40) #51 transformList (package:kernel/ast.dart:9699:27) #52 Library.transformChildren (package:kernel/ast.dart:627:5) #53 WidgetCreatorTracker.transform (package:kernel/transformations/track_widget_constructor_locations.dart:504:15) #54 FlutterTarget.performPreConstantEvaluationTransformations (package:vm/target/flutter.dart:82:22) #55 KernelTarget.runBuildTransformations (package:front_end/src/fasta/kernel/kernel_target.dart:1093:19) #56 KernelTarget.buildComponent. (package:front_end/src/fasta/kernel/kernel_target.dart:390:7) #57 KernelTarget.buildComponent. (package:front_end/src/fasta/kernel/kernel_target.dart) #58 withCrashReporting (package:front_end/src/fasta/crash.dart:122:24) #59 KernelTarget.buildComponent (package:front_end/src/fasta/kernel/kernel_target.dart:380:12) #60 generateKernelInternal. (package:front_end/src/kernel_generator_impl.dart:177:38) #61 generateKernelInternal. (package:front_end/src/kernel_generator_impl.dart) #62 withCrashReporting (package:front_end/src/fasta/crash.dart:122:24) #63 generateKernelInternal (package:front_end/src/kernel_generator_impl.dart:73:10) #64 kernelForProgramInternal. (package:front_end/src/api_prototype/kernel_generator.dart:61:35) #65 CompilerContext.runWithOptions. (package:front_end/src/fasta/compiler_context.dart:135:20) #66 CompilerContext.runWithOptions. (package:front_end/src/fasta/compiler_context.dart) #67 CompilerContext.runInContext.. (package:front_end/src/fasta/compiler_context.dart:123:46) #68 new Future.sync (dart:async/future.dart:223:31) #69 CompilerContext.runInContext. (package:front_end/src/fasta/compiler_context.dart:123:19) #70 _rootRun (dart:async/zone.dart:1190:13) #71 _CustomZone.run (dart:async/zone.dart:1093:19) #72 _runZoned (dart:async/zone.dart:1630:10) #73 runZoned (dart:async/zone.dart:1550:10) #74 CompilerContext.runInContext (package:front_end/src/fasta/compiler_context.dart:122:12) #75 CompilerContext.runWithOptions (package:front_end/src/fasta/compiler_context.dart:133:10) #76 kernelForProgramInternal (package:front_end/src/api_prototype/kernel_generator.dart:60:32) #77 kernelForProgram (package:front_end/src/api_prototype/kernel_generator.dart:52:17) #78 compileToKernel (package:vm/kernel_front_end.dart:381:28) #79 FrontendCompiler.compile. (package:frontend_server/frontend_server.dart:542:54) #80 new Future. (dart:async/future.dart:175:37) #81 _rootRun (dart:async/zone.dart:1182:47) #82 _CustomZone.run (dart:async/zone.dart:1093:19) #83 _CustomZone.runGuarded (dart:async/zone.dart:997:7) #84 _CustomZone.bindCallbackGuarded. (dart:async/zone.dart:1037:23) #85 _rootRun (dart:async/zone.dart:1190:13) #86 _CustomZone.run (dart:async/zone.dart:1093:19) #87 _CustomZone.bindCallback. (dart:async/zone.dart:1021:23) #88 Timer._createTimer. (dart:async-patch/timer_patch.dart:18:15) #89 _Timer._runTimers (dart:isolate-patch/timer_impl.dart:397:19) #90 _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:428:5) #91 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)

    Oops; flutter has exited unexpectedly: "FileSystemException: Cannot open file, path = 'build/flutter_assets/.last_build_id' (OS Error: No such file or directory, errno = 2)".

    A crash report has been written to /Users/local-admin/Documents/code2/aspectd/aspectd_impl/flutter_01.log. This crash may already be reported. Check GitHub for similar crashes. https://github.com/flutter/flutter/issues?q=is%3Aissue+FileSystemException%3A+Cannot+open+file%2C+path+%3D+%27build%2Fflutter_assets%2F.last_build_id%27+%28OS+Error%3A+No+such+file+or+directory%2C+errno+%3D+2%29

    To report your crash to the Flutter team, first read the guide to filing a bug. https://flutter.dev/docs/resources/bug-reports

    Create a new GitHub issue by pasting this link into your browser and completing the issue template. Thank you! https://git.io/JLXS0

    flutter doctor 信息:

    Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel unknown, 1.22.4, on macOS 11.1 20C69 darwin-x64, locale zh-Hans-CN) [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.1) [✓] Xcode - develop for iOS and macOS (Xcode 12.3) [✓] Chrome - develop for the web [✓] Android Studio (version 4.0) [✓] VS Code (version 1.52.1) [✓] Connected device (3 available)

    • No issues found!

    opened by ameryzhu 4
  • Fix build release

    Fix build release

    关于release下问题:

    因为release 下是aot模式,会进行全局的静态分析(TFA),代码逻辑在:

    //kernel_front_end.dart
    Future<KernelCompilationResults> compileToKernel(
        Uri source, CompilerOptions options,
        {bool includePlatform: false,
        bool aot: false,
        bool useGlobalTypeFlowAnalysis: false,
        Map<String, String> environmentDefines,
        bool enableAsserts: true,
        bool genBytecode: false,
        BytecodeOptions bytecodeOptions,
        bool dropAST: false,
        bool useProtobufTreeShaker: false,
        bool useProtobufTreeShakerV2: false,
        bool minimalKernel: false,
        bool treeShakeWriteOnlyFields: false,
        String fromDillFile: null}) async {
         ...
           // Run global transformations only if component is correct.
           //debug下不执行,release下aot为true会执行
      if ((aot || minimalKernel) && component != null) {
        await runGlobalTransformations(
            options.target,
            component,
            useGlobalTypeFlowAnalysis,
            enableAsserts,
            useProtobufTreeShaker,
            useProtobufTreeShakerV2,
            errorDetector,
            minimalKernel: minimalKernel,
            treeShakeWriteOnlyFields: treeShakeWriteOnlyFields);
            ...
      }
         ...
    
    }
    

    比如,执行flutter build apk命令,报错堆栈如下:

    Invalid argument(s): Iterables do not have same length.                 
    #0      MapBase._fillMapWithIterables (dart:collection/maps.dart:92:7)  
    #1      new LinkedHashMap.fromIterables (dart:collection/linked_hash_map.dart:127:13)
    #2      Substitution.fromPairs (package:kernel/type_algebra.dart:242:13)
    #3      DirectMethodInvocation.getStaticType (package:kernel/ast.dart:3542:25)
    #4      SummaryCollector._staticDartType (package:vm/transformations/type_flow/summary_collector.dart:1173:12)
    #5      SummaryCollector._staticType (package:vm/transformations/type_flow/summary_collector.dart:1176:36)
    #6      SummaryCollector._makeCall (package:vm/transformations/type_flow/summary_collector.dart:1080:26)
    #7      SummaryCollector.visitDirectMethodInvocation (package:vm/transformations/type_flow/summary_collector.dart:1519:12)
    #8      DirectMethodInvocation.accept (package:kernel/ast.dart:3528:44) 
    #9      SummaryCollector._visit (package:vm/transformations/type_flow/summary_collector.dart:853:42)
    #10     SummaryCollector.visitReturnStatement (package:vm/transformations/type_flow/summary_collector.dart:2058:37)
    #11     ReturnStatement.accept (package:kernel/ast.dart:6541:43)        
    #12     SummaryCollector._visit (package:vm/transformations/type_flow/summary_collector.dart:853:42)
    #13     List.forEach (dart:core-patch/growable_array.dart:313:8)        
    #14     SummaryCollector.visitBlock (package:vm/transformations/type_flow/summary_collector.dart:1923:21)
    #15     Block.accept (package:kernel/ast.dart:5873:43)                  
    #16     SummaryCollector._visit (package:vm/transformations/type_flow/summary_collector.dart:853:42)
    #17     SummaryCollector.createSummary (package:vm/transformations/type_flow/summary_collector.dart:758:9)
    #18     TypeFlowAnalysis.getSummary (package:vm/transformations/type_flow/analysis.dart:1451:52)
    #19     _DirectInvocation._processFunction (package:vm/transformations/type_flow/analysis.dart:287:42)
    #20     _DirectInvocation.process (package:vm/transformations/type_flow/analysis.dart:212:14)
    #21     _WorkList.processInvocation (package:vm/transformations/type_flow/analysis.dart:1347:32)
    #22     _DispatchableInvocation.process.<anonymous closure> (package:vm/transformations/type_flow/analysis.dart:437:44)
    #23     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:377:8)
    #24     _DispatchableInvocation.process (package:vm/transformations/type_flow/analysis.dart:412:12)
    #25     _WorkList.processInvocation (package:vm/transformations/type_flow/analysis.dart:1347:32)
    #26     TypeFlowAnalysis.applyCall (package:vm/transformations/type_flow/analysis.dart:1581:23)
    #27     Call.apply (package:vm/transformations/type_flow/summary.dart:265:31)
    #28     Summary.apply (package:vm/transformations/type_flow/summary.dart:702:33)
    #29     _DirectInvocation._processFunction (package:vm/transformations/type_flow/analysis.dart:296:24)
    #30     _DirectInvocation.process (package:vm/transformations/type_flow/analysis.dart:212:14)
    #31     _WorkList.processInvocation (package:vm/transformations/type_flow/analysis.dart:1347:32)
    #32     _DispatchableInvocation.process.<anonymous closure> (package:vm/transformations/type_flow/analysis.dart:437:44)
    #33     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:377:8)
    #34     _DispatchableInvocation.process (package:vm/transformations/type_flow/analysis.dart:412:12)
    #35     _WorkList.processInvocation (package:vm/transformations/type_flow/analysis.dart:1347:32)
    #36     TypeFlowAnalysis.applyCall (package:vm/transformations/type_flow/analysis.dart:1581:23)
    #37     Call.apply (package:vm/transformations/type_flow/summary.dart:265:31)
    #38     Summary.apply (package:vm/transformations/type_flow/summary.dart:702:33)
    #39     _DirectInvocation._processFunction (package:vm/transformations/type_flow/analysis.dart:296:24)
    #40     _DirectInvocation.process (package:vm/transformations/type_flow/analysis.dart:212:14)
    #41     _WorkList.processInvocation (package:vm/transformations/type_flow/analysis.dart:1347:32)
    #42     _DispatchableInvocation.process.<anonymous closure> (package:vm/transformations/type_flow/analysis.dart:437:44)
    #43     _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:377:8)
    #44     _DispatchableInvocation.process (package:vm/transformations/type_flow/analysis.dart:412:12)
    #45     _WorkList.processInvocation (package:vm/transformations/type_flow/analysis.dart:1347:32)
    #46     _WorkList.process (package:vm/transformations/type_flow/analysis.dart:1290:7)
    #47     TypeFlowAnalysis.process (package:vm/transformations/type_flow/analysis.dart:1469:14)
    #48     transformComponent (package:vm/transformations/type_flow/transformer.dart:78:20)
    #49     runGlobalTransformations (package:vm/kernel_front_end.dart:502:5)
    #50     compileToKernel (package:vm/kernel_front_end.dart:393:11)       
    <asynchronous suspension>                                               
    #51     FrontendCompiler.compile.<anonymous closure> (package:frontend_server/frontend_server.dart:542:54)
    #52     new Future.<anonymous closure> (dart:async/future.dart:175:37)  
    #53     _rootRun (dart:async/zone.dart:1182:47)                         
    #54     _CustomZone.run (dart:async/zone.dart:1093:19)                  
    #55     _CustomZone.runGuarded (dart:async/zone.dart:997:7)             
    #56     _CustomZone.bindCallbackGuarded.<anonymous closure> (dart:async/zone.dart:1037:23)
    #57     _rootRun (dart:async/zone.dart:1190:13)                         
    #58     _CustomZone.run (dart:async/zone.dart:1093:19)                  
    #59     _CustomZone.bindCallback.<anonymous closure> (dart:async/zone.dart:1021:23)
    #60     Timer._createTimer.<anonymous closure> (dart:async-patch/timer_patch.dart:18:15)
    #61     _Timer._runTimers (dart:isolate-patch/timer_impl.dart:397:19)   
    #62     _Timer._handleMessage (dart:isolate-patch/timer_impl.dart:428:5)
    #63     _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:168:12)
    

    可以看到是在TypeFlowAnalysis.process过程中报错,

    具体报错的地方:

      DartType getStaticType(StaticTypeContext context) {
       ...
        return Substitution.fromPairs(
                target.function.typeParameters, arguments.types)
            .substituteType(returnType);
      }
    

    因为传入fromPairs方法两个list参数的长度不一样,通过Map.fromIterables转化成Map就会报错。

    因为在构建DirectMethodInvocation的时候,对Argument参数的创建有问题:

    //创建DirectMethodInvocation
    final DirectMethodInvocation mockedInvocation = DirectMethodInvocation(
            AsExpression(PropertyGet(ThisExpression(), Name('target')),
                InterfaceType(originalClass, Nullability.legacy)),
            originalStubProcedure,
            AopUtils.concatArguments4PointcutStubCall(originalProcedure));
            
    //该方法对Arguments少了types的添加
     static Arguments concatArguments4PointcutStubCall(Member member) {
        final Arguments arguments = Arguments.empty();
         ...
          arguments.positional.add(asExpression);
          ...
          arguments.named.addAll(namedEntries);
          ...
     //缺少以下过程
        for(TypeParameter typeParameter in member.function.typeParameters){
        arguments.types.add(typeParameter.defaultType);
       }
        return arguments;
      }
    

    添加types字段虽然能解决长度不一致的问题,但是考虑到dart-sdk目前已经删除了DirectMethodInvocation等类,个人认为直接修改为MethodInvocation更佳。 image

    若分析有误或者不足还请见谅~

    opened by fightcoder 4
  • Inject方法可以用在自己写的方法中么

    Inject方法可以用在自己写的方法中么

    您好, 请问一下@inject可以注入到自己写的方法中么? 比方说 注入到 官方 example 的_incrementCounter 中, image 比方说想在【91】行写入一个输出

    ` @Inject("package:example/lib/main.dart","_MyHomePageState","-_incrementCounter",lineNum:91)

    @pragma("vm:entry-point")

    static void counterHook() {

    int count;//Aspectd Ignore

    int _counter;//Aspectd Ignore

    print("[KWLM12]:count:$count,counter:$_counter");

    //【91】行 }`

    但是无法注入 生成文件app.aspectd.dill.txt显示 如下图, image

    opened by kelciej 4
  • a package may not list itself as a dependency conflict

    a package may not list itself as a dependency conflict

    I have a flutter project called testproject I have created a dart package names aspectd_impl inside my project and in the pubspec I have added the dependency

    dependencies:
      flutter:
        sdk: flutter
      aspectd:
        git:
          url: https://github.com/alibaba-flutter/aspectd.git
          ref: master
      testproject:
        path: ../
    

    even if I make it to example it gives an error

    dependencies:
      flutter:
        sdk: flutter
      aspectd:
        git:
          url: https://github.com/alibaba-flutter/aspectd.git
          ref: master
      example:
        path: ../
    
    
    ../pubspec.yaml: "name" field doesn't match expected name "example".
    
    
    opened by maheshmnj 4
  • 在readComponent后直接writeComponentFile,报错,Reference to root::dart:collection is not bound to an AST node. A library was expected

    在readComponent后直接writeComponentFile,报错,Reference to root::dart:collection is not bound to an AST node. A library was expected

    在您的基础上尝试修改component时,read之后直接write就报错了.

    Unhandled exception: Reference to root::dart:collection is not bound to an AST node. A library was expected #请教下是什么原因呢

    Steps to Reproduce

    1.void readComponent(Component component, {bool checkCanonicalNames: false}) 2. void writeComponentFile(Component component)

    Logs

    Unhandled exception:
    Reference to root::dart:collection is not bound to an AST node. A library was expected
    #0      Reference.asLibrary (package:kernel/ast.dart:217:7)
    #1      _PrivateName.library (package:kernel/ast.dart:4764:38)
    #2      CanonicalName.getChildFromQualifiedName (package:kernel/canonical_name.dart:117:32)
    #3      CanonicalName.getChildFromMember (package:kernel/canonical_name.dart:123:10)
    #4      Class.computeCanonicalNames (package:kernel/ast.dart:917:21)
    #5      Library.computeCanonicalNames (package:kernel/ast.dart:423:14)
    #6      Component.computeCanonicalNamesForLibrary (package:kernel/ast.dart:5784:13)
    #7      Component.computeCanonicalNames (package:kernel/ast.dart:5778:7)
    

    Finally, paste the output of running flutter doctor -v here.

    Doctor summary (to see all details, run flutter doctor -v):
    [✓] Flutter (Channel stable, v1.5.4-hotfix.2, on Mac OS X 10.13.6 17G2208, locale zh-Hans-CN)
     
    [✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    [✓] iOS toolchain - develop for iOS devices (Xcode 10.1)
    [✓] Android Studio (version 3.4)
    [!] IntelliJ IDEA Ultimate Edition (version 2018.2.4)
        ✗ Flutter plugin not installed; this adds Flutter specific functionality.
        ✗ Dart plugin not installed; this adds Dart specific functionality.
    [✓] Connected device (1 available)
    
    
    opened by ckdgit 4
  • 在release模式下,call library级别的方法,编译出错。

    在release模式下,call library级别的方法,编译出错。

    @kangwang1988
    反复测试发现一个问题,我给一个library方法添加call aop,debug模式下能正常编译,而且运行结果也正确。release模式下,出现“Dart snapshot generator failed with exit code -6”错误,生成的app.dill文件,转为txt查看了下,也是正确的,但是最后在flutter/bin/flutter中出错。 以下是我的测试代码: image

    image

    错误日志: image

    opened by galaxybruce 4
  • Inject hook GestureDetector无效

    Inject hook GestureDetector无效

    我使用tag1.0.5的AspectD和flutter1.22.6,尝试像样例那样使用Inject hook GestureDetector 不成功。有人知道是什么原因吗?或者该怎么调试

    @Inject("package:flutter/src/widgets/gesture_detector.dart","GestureDetector","-build", lineNum:794) @pragma("vm:entry-point") static void onTapBuild() { Object instance; //Aspectd Ignore Object context; //Aspectd Ignore print(instance); print(context); print('---onTapBuild---'); }

    opened by lwy980328 0
  • 我适配了2.5.4,大大简化了注入逻辑

    我适配了2.5.4,大大简化了注入逻辑

    项目地址https://github.com/lancexin/aspect_frontend_server 现在暂时只支持execute模式

    该方法和aspectd的区别

    1. aspectd不支持flutter 2.5.3,本项目是基于flutter 2.5.3测试
    2. aspectd的编译需要对dart sdk中的vm进行修改,本项目不需要
    3. aspectd使用前需要对flutter tools的代码进行修改,本项目只需要替换flutter sdk对应的frontend_server.dart.snapshot即可
    4. aspectd的实现原理过于复杂,本项目去掉了Call,Inject等用法保留了Execute用法的同时对注入逻辑进行了简化
    5. aspectd还需要aspect_impl等,本项目可以直接在主程序代码中添加注入代码,也可以用plugin的方式添加
    6. 本项目不需要引入任何第三方包,用pragma注解完成对应插桩
    7. 可以有限制支持hot reload,完全支持hot restart,免去了冷重启的烦恼
    8. 为了性能优化inject方法限制必须是static的

    为什么用pragma注解,而不是自定义注解?

    本项目是在aot优化后再对字节码进行修改,aot优化后只有白名单中的注解才能被识别到,pragma是在白名单中的注解.

    opened by lancexin 7
Owner
null
Mysql.dart - MySQL client for Dart written in Dart

Native MySQL client written in Dart for Dart See example directory for examples

null 41 Sep 22, 2022
Flutter Navigation - all types of navigation in flutter run main.tabBar.dart to see tabBar, and run main.dart to see the otheres

pop_up_bar A new Flutter project. Getting Started This project is a starting point for a Flutter application. A few resources to get you started if th

Michael Odumosu 0 Jan 1, 2022
Flutter Dart-Projesi - Meditation app developed using Flutter and Dart

Müzik Çalar Meditasyon Uygulaması Medi Flutter ve Dart ile programlanmış bir med

null 2 Jan 29, 2022
A Dart client for the NATS messaging system. Design to use with Dart and Flutter.

Dart-NATS A Dart client for the NATS messaging system. Design to use with Dart and flutter. Flutter Web Support by WebSocket client.connect(Uri.parse(

Chart Chongcharoen 26 Aug 26, 2022
GitHub Action that uses the Dart Package Analyzer to compute the Pub score of Dart/Flutter packages

Dart/Flutter package analyzer This action uses the pana (Package ANAlysis) package to compute the score that your Dart or Flutter package will have on

Axel Ogereau-Peltier 44 May 17, 2022
🎬 A movie catalog app for both Android & IOS ~ Flutter.io project in Dart | Dart, Bloc, Movies

Movie Catalog App ?? Browse through movies from the YIFY api Getting Started For help getting started with Flutter, view our online documentation. Tod

Jonas De Vrient 46 Feb 23, 2022
Flying Fish is full-stack Dart framework - a semi-opinionated framework for building applications exclusively using Dart and Flutter

Flying Fish is full-stack Dart framework - a semi-opinionated framework for building applications exclusively using Dart and Flutter.

Flutter Fish 3 Jul 28, 2022
This is a dart package that converts words to numbers. It can be used in Flutter and normal Dart programs

Wordstonumbers.dart Wordstonumbers.dart is a simple dart package that converts a string of simple worded numbers into digits (e.g one hundred -> 100).

Michael Essiet 2 Sep 28, 2022
Docker images for the Dart programming language (https://dart.dev)

dart-docker This is the Git repo of the Docker "Official Images" for the Dart programming language. See the Docker Hub page for a full description on

Dart 45 Sep 28, 2022
A most easily usable Duolingo API wrapper in Dart. Duolingo4D is an open-sourced Dart library.

A most easily usable Duolingo API wrapper in Dart! 1. About Duolingo4D Duolingo4D is an open-sourced Dart library. With Duolingo4D, you can easily int

Kato Shinya 15 Jul 20, 2022
dna, dart native access. A lightweight dart to native super channel plugin

dna, dart native access. A lightweight dart to native super channel plugin, You can use it to invoke any native code directly in contextual and chained dart code.

Assuner 14 Jul 11, 2022
Dart package responsible to provide the basic resources to Lambda Functions with Clean Dart

AWS Lambda Core This package is responsible to provide the basic resources to all services; Usage pubspec.yaml dependencies: aws_lambda_core: <laste

David Araujo 1 Dec 2, 2021
Dart-com-Shelf - Web server básico feito com dart e shelf, configurações de rotas e conexão com mysql.

A server app built using Shelf, configured to enable running with Docker. This sample code handles HTTP GET requests to / and /echo/<message> Running

Luanzera07 0 Jan 3, 2022
Rock-Paper-Scissor-Game-Using-Dart - This is a repository of Rock Paper Scissor Game which I developed while learning Dart.

Rock-Paper-Scissor-Game-Using-Dart This is a repository of Rock Paper Scissor Game which I developed while learning Dart. The main.dart file consist o

Nitin Varma 0 Jan 4, 2022
Spider - A small dart library to generate Assets dart code from assets folder.

Spider A small dart library to generate Assets dart code from assets folder. It generates dart class with static const variables in it which can be us

Birju Vachhani 157 Sep 13, 2022
Tello-Dart - A testing ground for the Tello drone's socket API in dart

This packages provides a Dart interface to Ryze Tello drones. Getting started Ma

null 2 Jul 27, 2022
Socketio dart server and client - Full Socket.io implementation using Dart Lang

Getting Started Step 1: Run dart_server.dart Step 2: Android Emulator has proble

Trần Thiên Trọng 1 Jan 23, 2022
Dart wrapper via `dart:js` for webusb

Dart wrapper via dart:js for https://wicg.github.io/webusb/ Features canUseUsb g

Woodemi Co., Ltd 1 Jan 25, 2022