0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Flutter如何将代码显示到界面上呢?

OSC开源社区 来源:OSCHINA 社区 2023-05-10 10:04 次阅读

前言

如何优雅的将项目中的代码,亦或是你的 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

06191504-ee71-11ed-90ce-dac502259ad0.png

配置代码文件

因为原理是遍历资源文件,所以必须将需要展示的代码文件或者其文件夹路径,定义在 assets 下,这步操作,为大家提供了一个自动化的插件解决

强烈建议需要展示到界面的代码,都放在统一的文件夹里管理

展示界面的代码需要在 pugspec.yaml 中的 assets 定义

063e2df8-ee71-11ed-90ce-dac502259ad0.png

如果代码预览的文件夹,分级复杂,每次都需要定义路径实在麻烦

提供一个插件:Flutter Code Helper

安装:Plugins 中搜索Flutter Code Helper

066af856-ee71-11ed-90ce-dac502259ad0.png

pugspec.yaml 中定义下需要自动生成文件夹的路径,文件夹随便套娃,会自动帮你递归在 assets 下生成

不需要自动生成,可:不写该配置,或者配置空数组(auto_folder: [])

code_helper:
  auto_folder: [ "assets/", "lib/widgets/" ]

说明下:上面的插件是基于 RayC 的 FlutterAssetsGenerator 插件项目改的

看了下 RayC 的插件代码和相关功能,和我预想的上面功能实现有一定出入,改动起来变动较大

想试下插件项目的各种新配置,直接拉到最新

后期如果想到需要什么功能,方便随时添加

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

高级使用

主题

提供俩种代码样式主题

日间模式

CodePreview.config = CodePreviewConfig(codeTheme: CodeTheme.light);

06816ae6-ee71-11ed-90ce-dac502259ad0.png

夜间模式

CodePreview.config = CodePreviewConfig(codeTheme: CodeTheme.dark);

06a70c42-ee71-11ed-90ce-dac502259ad0.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 方案

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

大概看了下,整个 FlutterUnit 的数据都是基于flutter.db,该文件里面就有相关 demo 的文本信息

所有的 demo 也是单独存在一个叫widgets的项目中

所以大概可以猜测出

应该会有个 db 的辅助工具,会去扫描widgets的项目中的 demo 代码

将他们的文本信息都扫描出来,然后解析上面的注释等相关信息,分类存储到数据库中,最后生成 db 文件

06bf34fc-ee71-11ed-90ce-dac502259ad0.png

映射表,宿主可以通过 db 中的组件类名,从这里拿到 demo 效果实例

0700048c-ee71-11ed-90ce-dac502259ad0.png

总结

整套流程看下来,实现起来的工作量还是有点大的

db 辅助工具的编写

文本注释相关解析规则

如何便捷的维护 db 文件(辅助工具是否支持,生成后自动覆盖宿主 db 文件)

不同平台 db 文件的读取和相关适配

优点

因为扫描工具不依赖 Flutter 相关库,预览方案可以快速的移植到其它编程语言(compose,SwiftUI 等)

具备高度自定义,因为是完全独立的第三方扫描工具,可以随性所欲的定制化

缺点

最明显的缺点,应该就是稍微改下 demo 代码,就需要三方工具重新生成 db 文件(如果三方工具实现的是 cli 工具,可以将扫描生成命令和 push 等命令集成一起,应该可以比较好的避免该问题)

build_runner 方案

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');

072a8b8a-ee71-11ed-90ce-dac502259ad0.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,这样比较好对照

07509834-ee71-11ed-90ce-dac502259ad0.png

路径定义优化

本来是想在pubspec.yaml的assets里面直接写通配符定义全路径,然后悲剧了,它不支持这种写法

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

GG,只能想其他办法了,想了很多方法都不行,只能从外部入手,用 idea 插件的形式,实现自动化扫描生成路径

安装:Plugins 中搜索Flutter Code Helper

066af856-ee71-11ed-90ce-dac502259ad0.png

pugspec.yaml 中定义下需要自动生成文件夹的目录,文件夹随便套娃,会自动帮你递归在 assets 下生成

不需要自动生成,可:不写该配置,或者配置空数组(auto_folder: [])

code_helper:
  auto_folder: [ "assets/", "lib/widgets/" ]

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,并不一定会得到预期内的数据。。。

解决思路

将压缩类型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 平台,可以方便的体验各种组件的效果,结合文章中的代码预览方案,就可以更加快速的上手各种组件用法了~





审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Rayc
    +关注

    关注

    0

    文章

    2

    浏览量

    6007
  • flutter
    +关注

    关注

    0

    文章

    12

    浏览量

    407

原文标题:Flutter如何将代码显示到界面上

文章出处:【微信号:OSC开源社区,微信公众号:OSC开源社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    请问STM8S如何将中断代码拷贝RAM中运行?

    STM8S在写EEPROM时,中断停掉,如何将中断代码拷贝RAM中运行??? 求助各位大神!!!
    发表于 05-17 07:37

    深入理解flutter的编译原理与优化

    摘要: 闲鱼技术-正物 问题背景 对于开发者而言,什么是Flutter?它是用什么语言编写的,包含哪几部分,是如何被编译,运行设备上的Flutter如何做到Debug模式Hot
    发表于 07-02 17:47

    如何将代码集成Multi IDE Project?

    我想知道是否可以使用PinMap向导生成C代码以与Multi IDE Green Hill编译器一起使用,如果可以的话,如何将代码集成Multi IDE Project? 提前致
    发表于 06-21 10:05

    界面上显示和输入控件特别多时如何管理

    如果在一个界面上有几十个控件(显示和输入都有),类型还不一样,请问如何管理这些控件,现在我都是用很多事件结构来做的,请问大家有什么好的方法?特别是在ActorFramework框架中。
    发表于 09-09 19:32

    如何将现有代码一点点移植HC32F460上

    如何将现有代码一点点移植HC32F460上?其移植过程是怎样的?
    发表于 11-25 06:27

    如何将STM32的代码以日期作为软件版本号去实现

    如何将STM32的代码以日期作为软件版本号去实现?求大神解答
    发表于 12-15 06:20

    如何将gdb移植嵌入式板子上单独运行

    如何在Linux下去调试C++执行程序如何将gdb移植嵌入式板子上单独运行
    发表于 12-24 06:57

    如何将显示缓冲区的数据取出拼装成一个字节

    如何将显示缓冲区的数据取出拼装成一个字节?如何修改程序
    发表于 01-21 07:34

    如何将ADC采集的数据发送到PC端

    如何将ADC采集的数据发送到PC端
    发表于 01-25 07:09

    如何将示例flutter应用程序安装到STM32MP157F-DK2?

    /environment-setup-cortexa7t2hf-neon-vfpv4-ostl-linux-gnueabiexport CC=${CLANGCC}export CXX=${CLANGCXX}我的问题是如何将一个简单的 flutter 应用程序安装到我的发现
    发表于 12-06 07:14

    如何将ST25dv sdk移植Flutter

    我想开发一个简单的应用程序来读/写 ST25DV 设备的内部 EEPROM。是否有 ST25dv sdk Flutter 的移植?
    发表于 01-03 09:01

    如何将BLDC进电机控制算法移植STM微控制器上

    为 STSPIN 提供的 6 步算法。我们应该将该代码移植到任何其他 ST 控制器吗?如何将BLDC进电机控制算法移植STM微控制器上
    发表于 01-17 08:54

    如何将X-CUBE-NFC5代码集成平台中?

    ,我只是主要的 .c 和 .h 文件从 RFAL 和 BSP 文件夹复制项目中,但出现了很多错误。是否有关于如何将代码添加到项目或我需要使用哪些软件来使用 X-CUBE-NFC5
    发表于 02-02 07:22

    如何将ADI公司的Spice模型导入Multisim仿真软件

    请问如何将ADI公司的Spice模型导入Multisim仿真软件? 当我导入时为什么找不到该文件
    发表于 11-17 06:20

    Flutter 共创未来 | Flutter Forward 活动精彩回顾

    Flutter Forward 活动 https://flutter.dev/events/flutter-forward Flutter 是一个
    的头像 发表于 02-22 23:20 446次阅读