前言

怎么优雅的将项目中代码,亦或许你的demo代码展现到界面上?本文对运用简略、便于保护且通用的解决计划,进行相关的比照和探求

为了节约我们的时刻,把终究解决计划的相关接入和用法写在前面

预览代码

快速开始

  • 接入:pub,github
dependencies:
  code_preview: ^0.1.5
  • 用法:CodePreview,提供需求预览的className,可主动匹配该类对应的代码文件
    • 原本想把写法简化成传入对象,可是因为一些原因无法放弃,改成了className
    • 详细能够参考下面Flutter Web中的问题模块的阐明
import 'package:code_preview/code_preview.dart';
import 'package:flutter/material.dart';
class Test extends StatelessWidget {
  const Test({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const CodePreview(className: 'Test');
  }
}
  • 运用作用:flutter_smart_dialog

image.png

装备代码文件

因为原理是遍历资源文件,所以有必要将需求展现的代码文件或许其文件夹途径,界说在assets下,这步操作,为我们提供了一个主动化的插件解决

强烈建议需求展现到界面的代码,都放在一致的文件夹里管理

  • 展现界面的代码需求在pugspec.yaml中的assets界说

image.png

假如代码预览的文件夹,分级杂乱,每次都需求界说途径真实费事

提供一个插件:Flutter Code Helper

  • 安装:Plugins中查找Flutter Code Helper

image.png

  • pugspec.yaml中界说下需求主动生成文件夹的途径,文件夹随意套娃,会主动帮你递归在assets下生成
    • 不需求主动生成,可:不写该装备,或许装备空数组(auto_folder: [])
code_helper:
  auto_folder: [ "assets/", "lib/widgets/" ]

202304292234691.gif

阐明下:上面的插件是根据RayC的FlutterAssetsGenerator插件项目改的

  • 看了下RayC的插件代码和相关功用,和我预想的上面功用完成有必定收支,改动起来变动较大
  • 想试下插件项目的各种新装备,直接拉到最新
  • 后期假如想到需求什么功用,方便随时增加

所以没向其插件里边提pr,就独自新开了个插件项目

高级运用

主题

提供俩种代码样式主题

  • 日间形式
CodePreview.config = CodePreviewConfig(codeTheme: CodeTheme.light);

image.png

  • 夜间形式
CodePreview.config = CodePreviewConfig(codeTheme: CodeTheme.dark);

image.png

注释解析

  • 你能够运用如下的格局,在类上增加注释
    • key的前面有必要加@,举例(@title,@xxx)
    • key与value的之间,有必要运用分号分割,举例(@xxx: xxx)
    • value假如需求换行,换行的文案前有必要加中划线
/// @title:
///  - test title one
///  - test title two
/// @content: test content
/// @description: test description
class OneWidget extends StatelessWidget {
  const OneWidget({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const Placeholder();
  }
}
  • 然后能够从customBuilder的回调获取param参数,param中拥有parseParam参数
    • 获取获得上面注释的数据:param.parseParam[‘title’]或许param.parseParam[‘***’]
    • 获取的value的类型是List,可兼容多行value的类型
  • customBuilder的用法

    • codeWidget内置的代码预览布局,假如你想界说自己预览代码的布局,那就能够不运用codeWidget
    • 一般来说,能够根据注释获取的数据,结合codeWidget嵌套来自界说符合要求的布局
    • param中含有多个有用内容,可自行查看
import 'package:code_preview/code_preview.dart';
import 'package:flutter/material.dart';
class Test extends StatelessWidget {
  const Test({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return CodePreview(
      className: 'OneWidget',
      customBuilder: (Widget codeWidget, CustomParam? param) {
        debugPrint(param?.parseParam['title'].toString());
        debugPrint(param?.parseParam['content'].toString());
        debugPrint(param?.parseParam['description'].toString());
        return codeWidget;
      },
    );
  }
}
  • 现在内部预览的布局,会主动去掉类上的注释,假如想保存注释,可自行装备下
 CodePreview.config = CodePreviewConfig(removeParseComment: false);

几种代码预览计划

FlutterUnit计划

  • github.com/toly1994328…

FlutterUnit项目也是自带代码预览计划,这套计划是比较特殊计划

  • 大概看了下,整个FlutterUnit的数据都是根据flutter.db,该文件里边就有相关demo的文本信息
  • 一切的demo也是独自存在一个叫widgets的项目中
  • 所以大概能够猜测出
    • 应该会有个db的辅助东西,会去扫描widgets的项目中的demo代码
    • 将他们的文本信息都扫描出来,然后解析上面的注释等相关信息,分类存储到数据库中,最终生成db文件

image.png

  • 映射表,宿主能够经过db中的组件类名,从这儿拿到demo作用实例

image.png

总结

整套流程看下来,完成起来的工作量仍是有点大的

  • db辅助东西的编写
  • 文本注释相关解析规则
  • 怎么便捷的保护db文件(辅助东西是否支持,生成后主动覆盖宿主db文件)
  • 不同渠道db文件的读取和相关适配

长处

  • 因为扫描东西不依靠Flutter相关库,预览计划能够快速的移植到其它编程言语(compose,SwiftUI等)
  • 具备高度自界说,因为是彻底独立的第三方扫描东西,能够随性所欲的定制化

缺陷

  • 最显着的缺陷,应该便是稍微改下demo代码,就需求三方东西重新生成db文件(假如三方东西完成的是cli东西,能够将扫描生成指令和push等指令集成一同,应该能够比较好的避免该问题)

build_runner计划

  • pub.dev/packages/bu…

build_runner是个强壮代码主动生成东西,根据ast语法树+自界说注解信息,能够生成许多强壮的隶属代码信息,例如 json_serializable等库

所以,也能运用这点自界说类注解,获取到对应的整个类的代码信息,在对应隶属的xx.g.dart文件中,将获取的代码内容转换成字符串,然后直接将xx.g.dart文件的代码字符串信息,展现到界面就行了

长处

  • 能够经过生成指令,全主动的生成代码,乃至将整个预览demo的映射表都能够主动装备完成
  • 能够规范的经过注解装备多个参数

缺陷

  • 因为build_runner需求解析整个ast语法树,一旦项目很大之后,解析生成的时刻会十分十分的长!
  • 因为现在许多的这类库都是依靠build_runner,所以跑主动生成指令,会导致巨多xx.g.dart文件被改动,极大的增加cr工作量

资源文件计划

这应该最常用的一种计划

  • pubspec.yaml中的assets中界说下咱们代码文件途径
flutter:
  assets:
    - lib/widgets/show/
  • 然后用loadString获取文件内容
final code = await rootBundle.loadString('lib/widgets/show/custome_dialog_animation.dart');

image.png

长处

  • 侵入性十分低,不会像build_runnner计划那样影响到其它模块
  • 便于保护,假如demo预览代码被改动了,打包的时分,资源文件也会生成对应改动后的代码文件

缺陷

  • 运用费事,运用的时分需求传入详细的文件途径,才干找到想要的代码资源文件
  • 需求反复的在pubspec.yaml中的assets里边界说文件途径

资源文件计划优化

上面的三种计划各有优缺陷,清晰当前的诉求

  • 现在是想写个简略的,通用的,仅在Flutter中完成代码预览计划

  • 要求运用简略,高效

  • 保护简略,多人开发的时分不会有很大成本

FlutterUnit计划:完成起来成本较大,且多人开发对单个db文件的保护很可能会有点问题,例如:更新代码的时分,db文件忘记更新

build_runner计划:生成时刻是个问题,还有很对其他类型xx.g.dart文件发生影响也比较费事

资源文件计划:整体是符合预期的,可是运用时分,需求传入途径和pubspec.yaml中反复界说文件途径,这是俩个很大痛点

结合完成成本和诉求,挑选资源文件计划,下面临其痛点进行优化

运用优化

Flutter的编译产品中,有个相当有用的文件:AssetManifest.json

AssetManifest.json文件里边,有一切的资源文件的途径,然后就简略了,咱们只需求读取该文件内容

final manifestContent = await rootBundle.loadString('AssetManifest.json');

获取到一切的途径之后,再结合传入的类名,读取一切途径的文件内容,然后和传入的类名做正则匹配就行了

稍微优化

  • 将传入的类名,转换为下划线称号和一切途径称号做匹配,假如能匹配上,再进行内容匹配,匹配成功后就返回该文件的代码内容
  • 假如上述匹配失败,就进行兜底的全量匹配

优化前

import 'package:code_preview/code_preview.dart';
import 'package:flutter/material.dart';
class Test extends StatelessWidget {
  const Test({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const CodePreview(path: 'lib/widgets/show/custome_dialog_animation.dart');
  }
}

优化后

import 'package:code_preview/code_preview.dart';
import 'package:flutter/material.dart';
class Test extends StatelessWidget {
  const Test({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return const CodePreview(className: 'CustomDialogAnimation');
  }
}
  • 一般来说,我是一致装备预览demo和className,这样比较好对照

image.png

途径界说优化

原本是想在pubspec.yamlassets里边直接写通配符界说全途径,然后悲惨剧了,它不支持这种写法

flutter:
  assets:
    - lib/widgets/**/*.dart

GG,只能想其他办法了,想了许多办法都不可,只能从外部下手,用idea插件的形式,完成主动化扫描生成途径

  • 安装:Plugins中查找Flutter Code Helper

image.png

  • pugspec.yaml中界说下需求主动生成文件夹的目录,文件夹随意套娃,会主动帮你递归在assets下生成
    • 不需求主动生成,可:不写该装备,或许装备空数组(auto_folder: [])
code_helper:
  auto_folder: [ "assets/", "lib/widgets/" ]

202304292234691.gif

Flutter Web中的问题

魔幻的runtimeType

flutter web的release形式中

  • dart2js 会紧缩 JS,这样会使得类型名被改动
  • 例如:dart中的TestWidgetFunction类的runtimeType,可能会变成minified:Ah,而不是TestWidgetFunction

为啥需求紧缩呢?紧缩称号能够使得编译器将 JavaScript体积缩小 3 倍+;准确等效语义和性能/代码大小之间的权衡,Dart显着是挑选了后者

这种情况只会在Flutter Web的release形式下发生,其他渠道和Flutter web的Debug | Profile形式都不会有这种问题;所以说Xxx.runtimeType.toString,并不必定会得到预期内的数据。。。

详细讨论可参考

  • github.com/dart-lang/s…
  • github.com/flutter/flu…

解决思路

  • 将紧缩类型minified:Ah 恢复成 Test
  • 将获取的Test字符串运用相同算法紧缩成minified:Ah

如有知道怎么完成的,必须告诉鄙人

下面从紧缩等级调整的角度,探求是否可解决该问题

dart2js紧缩阐明

注:flutter build web默许的是O4优化等级

  • O0: 禁用许多优化。
  • O1: 启用默许优化(仅是dart2js该指令的默许等级)
  • O2: 在O1优化基础上,尊重言语语义且对一切程序安全的其他优化(例如缩小)
    • 备注:运用-O2,运用开发JavaScript编译器编译时,类型的字符串表明不再与Dart VM中的字符串表明相同
  • O3: 在O2优化基础上,并省掉隐式类型查看。
    • 留意:省掉类型查看可能会导致应用程序因类型错误而溃散
  • O4: 在O3优化基础上,启用更活跃的优化
    • 留意:O4优化容易遭到输入数据改动的影响,在依靠O4之前,需测试用户输入中的边际情况

下面是flutter新建项目,未做任何改动,不同紧缩等级的js产品体积

# main.dart.js: 7.379MB
flutter build web --dart2js-optimization O0 
# main.dart.js: 5.073MB
flutter build web --dart2js-optimization O1
# main.dart.js: 1.776MB
flutter build web --dart2js-optimization O2
# main.dart.js: 1.716MB
flutter build web --dart2js-optimization O3
# main.dart.js: 1.687MB
flutter build web --dart2js-optimization O4

总结

  • 预期用法
    • 为什么想运用对象?因为当对象称号改动时,对应运用的地方,能够便捷观察到需求改动
    • 能够运用传入的对象实例,在内部运用runtimeType获取类型名,再进行相关匹配
CodePreview(code: Test());

可是

综上可知,运用flutter build web --dart2js-optimization O1编译的flutter web release产品,能够使得runtimeType的语义和Dart VM中字符串保持一致

可是该紧缩等级下的,js体积过于夸张,必须会对加载速度发生极大影响,可想而知,在杂乱项目中的体积增涨肯定愈加离谱

关于想要用法愈加简略,运用低等级紧缩指令打包的想法需求舍弃

  • 用法不得已做退让
CodePreview(className: "Test");

这是个让我十分纠结的思路进程

最终

到这儿也结束了,自我感觉,对我们应该能有一些帮助

一般来说,大部分团队,都会有个自己的内部组件库,因为Flutter强壮的跨渠道特性,所以就能很轻松的发布到web渠道,能够方便的体会各种组件的作用,结合文章中的代码预览计划,就能够愈加快速的上手各种组件用法了~

好了,下次再见了,靓仔们!