Dart-通過(guò)注解自動(dòng)生成代碼(SourceGen、BuildRunner)

使用過(guò)Dart自動(dòng)Json序列化的都用過(guò)自動(dòng)生成代碼俏橘,但是你知道如何自己自定義注解并生成代碼嗎允华?

  1. 需要使用 source_gen、build_runner 這兩個(gè)庫(kù)寥掐;
  2. 定義注解類例获,配置注解到自動(dòng)代碼的Generator類及Builder類;

綜上所述曹仗,咱需要構(gòu)建兩個(gè)Library庫(kù)來(lái)使用注解自動(dòng)化代碼生成榨汤。

Library1: 注解類定義

Library2: 注解類的代碼自動(dòng)生成Generator及Builder

下面我們就說(shuō)說(shuō)怎么來(lái)做吧!

首先怎茫,假如我們需要實(shí)現(xiàn)一個(gè)自動(dòng)生成MethodChannel調(diào)用Native方法的一個(gè)功能收壕,這里我們叫它NativeCall吧。

  1. 新建NativeCall庫(kù)轨蛤,編寫NativeCall注解的類蜜宪。

    import 'package:native_call/native_method_call_info.dart';
    
    class NativeCall {
      /// ChannelName
      final String channelName;
      const NativeCall({this.channelName}); //注解類,構(gòu)造函數(shù)必須是Const的
    }
    
  1. 新建NativeCallGen庫(kù)祥山,編寫NativeCallGenerator, NativeCallBuilder圃验,配置builder.yaml,該庫(kù)需要依賴上面的NativeCall缝呕,需要引用第三方的庫(kù)(即yaml文件中配置:SourceGen澳窑, BuildRunner)

    dependencies:
      flutter:
        sdk: flutter
      source_gen: ^0.9.6 #需要包含代碼自動(dòng)庫(kù)
      native_call:  #需要包含注解的庫(kù)
        path: ../native_call 
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
      build_runner: ^1.10.0  #需要引用該庫(kù)
    

NativeCallGenerator斧散,就是用來(lái)生成代碼的,可以通過(guò)設(shè)置模板代碼字符串等來(lái)輸出摊聋。
下面通過(guò)解析ClassElement以及其內(nèi)部的MethodElement來(lái)進(jìn)行解析并得到需要自動(dòng)化的代碼內(nèi)容字符串鸡捐。
自定義類需要繼承GeneratorForAnnotation類,并實(shí)現(xiàn)generateForAnnotationElement方法麻裁,該方法內(nèi)即可編寫相應(yīng)的代碼生成邏輯箍镜。

library native_call_gen;

import 'package:path/path.dart' as Path;
import 'package:native_call/native_call.dart';
import 'package:source_gen/source_gen.dart';
import 'package:analyzer/dart/element/element.dart';
// ignore: implementation_imports
import 'package:build/src/builder/build_step.dart' show BuildStep;

class NativeCallGenerator extends GeneratorForAnnotation<NativeCall> {
  @override
  generateForAnnotatedElement(
      Element element, ConstantReader annotation, BuildStep buildStep) {
    if (element is! ClassElement)
      throw InvalidGenerationSourceError('注解未使用在類上');

    String className = element.displayName;
    String path = buildStep.inputId.path;
    String channelName = annotation.peek('channelName').stringValue;

    StringBuffer sb = StringBuffer();

    (element as ClassElement).methods?.forEach((el) {
      var mName =
          String.fromCharCode(el.displayName.codeUnitAt(0)).toUpperCase() +
              el.displayName.substring(1);
      StringBuffer paramsStr = StringBuffer();
      StringBuffer params = StringBuffer();
      el.parameters?.forEach((param) {
        paramsStr.write('${param.type} ${param.name},');
        params.write('\'${param.name}\':' + param.name + ',');
      });
      sb.writeln('''
      ${el.hasImplicitReturnType == true ? 'Future<dynamic>' : 'void'} \$$className$mName(${paramsStr.toString()}) =>
       _methodChannel.invokeMethod('${el.displayName}', { ${params.toString()} });
      ''');
    });
    return '''
    part of '${Path.basename(buildStep.inputId.path)}';
    
    final MethodChannel _methodChannel = const MethodChannel('$channelName');


    ${sb.toString()}

    

    ''';
  }
}

NativeCallBuilder 生成代碼的構(gòu)造器,到時(shí)候build.yaml文件中會(huì)用到煎源。

import 'package:native_call_gen/native_call_generator.dart';
import 'package:source_gen/source_gen.dart';
import 'package:build/build.dart';

Builder nativeCallBuilder(BuilderOptions options) =>
    LibraryBuilder(NativeCallGenerator(), generatedExtension: '.nc.g.dart');

build.yaml文件的配置

targets:
  $default: #定義目標(biāo)庫(kù)色迂,關(guān)鍵字$default默認(rèn)為當(dāng)前庫(kù)
    builders:
     natice_call_gen|native_call:  
        enabled: true #可選,是否將構(gòu)建器應(yīng)用于此目標(biāo)
     source_gen|combining_builder:
      enabled: true

builders:
  native_call_builder:
    target: ":native_call_gen" #目標(biāo)庫(kù)
    import: 'package:native_call_gen/native_call_builder.dart'  #build文件
    builder_factories: ['nativeCallBuilder']
    build_extensions: {'.dart': ['.nc.g.dart']}
    auto_apply: dependents #將此Builder應(yīng)用于包手销,直接依賴于公開(kāi)構(gòu)建起的包脚草,也可以是root_package
    build_to: source #輸出到注解的目標(biāo)類的代碼同目錄中,或者輸出轉(zhuǎn)到隱藏的構(gòu)建緩存原献,不會(huì)發(fā)布(cache)
    applies_builders: ["source_gen|combining_builder"] #指定是否可以延遲運(yùn)行構(gòu)建器
    # 以上參數(shù)具體參考 https://github.com/dart-lang/build/blob/master/build_config/README.md
  1. 新建一個(gè)測(cè)試項(xiàng)目馏慨,并引入NativeCallNativeCallGen庫(kù),配置如下:

    dependencies:
      flutter:
        sdk: flutter
      cupertino_icons: ^0.1.3
      native_call:  #引入NativeCall
        path: ../native_call/
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
      build_runner: ^1.10.0 #引入builder_runner
      native_call_gen:  #引入NativeCallGen
        path: ../native_call_gen/
    

    測(cè)試類:AppNativeUtils

    import 'package:native_call/native_call.dart';
    import 'package:flutter/services.dart';
    
    part 'app_native_utils.nc.g.dart';  //包含該自動(dòng)生成的part代碼
    
    @NativeCall(channelName: 'com.china.mrper/utils/app_utils')  //使用NativeCall注解
    class AppNativeUtils {
      void showToast(String message, [int length]) =>  //定義toast方法
          $AppNativeUtilsShowToast(message, length);
    
      void showAlertDialog(String title, {message: String}) => //定義showAlertDialog的方法
          $AppNativeUtilsShowAlertDialog(title, message);
    }
    
    

    終端執(zhí)行命令行姑隅,如下:**flutter package pub run build_runner build **

    生成如下代碼:app_native_utils.nc.g.dart

    // GENERATED CODE - DO NOT MODIFY BY HAND
    
    // **************************************************************************
    // NativeCallGenerator
    // **************************************************************************
    
    part of 'app_native_utils.dart';
    
    final MethodChannel _methodChannel =
        const MethodChannel('com.china.mrper/utils/app_utils');
    
    void $AppNativeUtilsShowToast(
      String message,
      int length,
    ) =>
        _methodChannel.invokeMethod('showToast', {
          'message': message,
          'length': length,
        });
    
    void $AppNativeUtilsShowAlertDialog(
      String title,
      dynamic message,
    ) =>
        _methodChannel.invokeMethod('showAlertDialog', {
          'title': title,
          'message': message,
        });
    
    

至此写隶,所有的流程已經(jīng)完成,如果你覺(jué)得對(duì)你有用讲仰,請(qǐng)點(diǎn)個(gè)贊慕趴!
其他:轉(zhuǎn)載請(qǐng)注明出處,感謝鄙陡!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末冕房,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子趁矾,更是在濱河造成了極大的恐慌耙册,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毫捣,死亡現(xiàn)場(chǎng)離奇詭異详拙,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蔓同,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門饶辙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人斑粱,你說(shuō)我怎么就攤上這事弃揽。” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵矿微,是天一觀的道長(zhǎng)痕慢。 經(jīng)常有香客問(wèn)我,道長(zhǎng)冷冗,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任惑艇,我火速辦了婚禮蒿辙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘滨巴。我一直安慰自己思灌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布恭取。 她就那樣靜靜地躺著泰偿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜈垮。 梳的紋絲不亂的頭發(fā)上耗跛,一...
    開(kāi)封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音攒发,去河邊找鬼调塌。 笑死,一個(gè)胖子當(dāng)著我的面吹牛惠猿,可吹牛的內(nèi)容都是我干的羔砾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼偶妖,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼姜凄!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起趾访,我...
    開(kāi)封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤态秧,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后扼鞋,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體屿聋,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年藏鹊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了润讥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡盘寡,死狀恐怖楚殿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤脆粥,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布砌溺,位于F島的核電站,受9級(jí)特大地震影響变隔,放射性物質(zhì)發(fā)生泄漏规伐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一匣缘、第九天 我趴在偏房一處隱蔽的房頂上張望猖闪。 院中可真熱鬧,春花似錦肌厨、人聲如沸培慌。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吵护。三九已至,卻和暖如春表鳍,著一層夾襖步出監(jiān)牢的瞬間馅而,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工譬圣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留用爪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓胁镐,卻偏偏與公主長(zhǎng)得像偎血,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盯漂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355