前言
如何優(yōu)雅的將項(xiàng)目中的代碼,亦或是你的 demo 代碼展示到界面上?本文對(duì)使用簡(jiǎn)單、便于維護(hù)且通用的解決方案,進(jìn)行相關(guān)的對(duì)比和探究
為了節(jié)省大家的時(shí)間,把最終解決方案的相關(guān)接入和用法寫在前面
預(yù)覽代碼
快速開始
接入:pub,github
dependencies: code_preview: ^0.1.5
用法:CodePreview,提供需要預(yù)覽的 className,可自動(dòng)匹配該類對(duì)應(yīng)的代碼文件
本來想把寫法簡(jiǎn)化成傳入對(duì)象,但是因?yàn)橐恍┰驘o奈放棄,改成了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
配置代碼文件
因?yàn)樵硎潜闅v資源文件,所以必須將需要展示的代碼文件或者其文件夾路徑,定義在 assets 下,這步操作,為大家提供了一個(gè)自動(dòng)化的插件解決
強(qiáng)烈建議需要展示到界面的代碼,都放在統(tǒng)一的文件夾里管理
展示界面的代碼需要在 pugspec.yaml 中的 assets 定義
如果代碼預(yù)覽的文件夾,分級(jí)復(fù)雜,每次都需要定義路徑實(shí)在麻煩
提供一個(gè)插件:Flutter Code Helper
安裝:Plugins 中搜索Flutter Code Helper
pugspec.yaml 中定義下需要自動(dòng)生成文件夾的路徑,文件夾隨便套娃,會(huì)自動(dòng)幫你遞歸在 assets 下生成
不需要自動(dòng)生成,可:不寫該配置,或者配置空數(shù)組(auto_folder: [])
code_helper: auto_folder: [ "assets/", "lib/widgets/" ]
說明下:上面的插件是基于 RayC 的 FlutterAssetsGenerator 插件項(xiàng)目改的
看了下 RayC 的插件代碼和相關(guān)功能,和我預(yù)想的上面功能實(shí)現(xiàn)有一定出入,改動(dòng)起來變動(dòng)較大
想試下插件項(xiàng)目的各種新配置,直接拉到最新
后期如果想到需要什么功能,方便隨時(shí)添加
所以沒向其插件里面提 pr,就單獨(dú)新開了個(gè)插件項(xiàng)目
高級(jí)使用
主題
提供倆種代碼樣式主題
日間模式
CodePreview.config = CodePreviewConfig(codeTheme: CodeTheme.light);
夜間模式
CodePreview.config = CodePreviewConfig(codeTheme: CodeTheme.dark);
注釋解析
你可以使用如下的格式,在類上添加注釋
key 的前面必須加@,舉例(@title,@xxx)
key 與 value 的之間,必須使用分號(hào)分割,舉例(@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的回調(diào)獲取 param 參數(shù),param 中擁有 parseParam 參數(shù)
獲取取得上面注釋的數(shù)據(jù):param.parseParam ['title'] 或者 param.parseParam ['***']
獲取的 value 的類型是 List
customBuilder的用法
codeWidget內(nèi)置的代碼預(yù)覽布局,如果你想定義自己預(yù)覽代碼的布局,那就可以不使用codeWidget
一般來說,可以根據(jù)注釋獲取的數(shù)據(jù),結(jié)合codeWidget嵌套來自定義符合要求的布局
param中含有多個(gè)有用內(nèi)容,可自行查看
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; }, ); } }
目前內(nèi)部預(yù)覽的布局,會(huì)自動(dòng)去掉類上的注釋,如果想保留注釋,可自行匹配下
CodePreview.config = CodePreviewConfig(removeParseComment: false);
幾種代碼預(yù)覽方案
FlutterUnit 方案
FlutterUnit 項(xiàng)目也是自帶代碼預(yù)覽方案,這套方案是比較特殊方案
大概看了下,整個(gè) FlutterUnit 的數(shù)據(jù)都是基于flutter.db,該文件里面就有相關(guān) demo 的文本信息
所有的 demo 也是單獨(dú)存在一個(gè)叫widgets的項(xiàng)目中
所以大概可以猜測(cè)出
應(yīng)該會(huì)有個(gè) db 的輔助工具,會(huì)去掃描widgets的項(xiàng)目中的 demo 代碼
將他們的文本信息都掃描出來,然后解析上面的注釋等相關(guān)信息,分類存儲(chǔ)到數(shù)據(jù)庫中,最后生成 db 文件
映射表,宿主可以通過 db 中的組件類名,從這里拿到 demo 效果實(shí)例
總結(jié)
整套流程看下來,實(shí)現(xiàn)起來的工作量還是有點(diǎn)大的
db 輔助工具的編寫
文本注釋相關(guān)解析規(guī)則
如何便捷的維護(hù) db 文件(輔助工具是否支持,生成后自動(dòng)覆蓋宿主 db 文件)
不同平臺(tái) db 文件的讀取和相關(guān)適配
優(yōu)點(diǎn)
因?yàn)閽呙韫ぞ卟灰蕾?Flutter 相關(guān)庫,預(yù)覽方案可以快速的移植到其它編程語言(compose,SwiftUI 等)
具備高度自定義,因?yàn)槭峭耆?dú)立的第三方掃描工具,可以隨性所欲的定制化
缺點(diǎn)
最明顯的缺點(diǎn),應(yīng)該就是稍微改下 demo 代碼,就需要三方工具重新生成 db 文件(如果三方工具實(shí)現(xiàn)的是 cli 工具,可以將掃描生成命令和 push 等命令集成一起,應(yīng)該可以比較好的避免該問題)
build_runner 方案
build_runner 是個(gè)強(qiáng)大代碼自動(dòng)生成工具,根據(jù) ast 語法樹 + 自定義注解信息,可以生成很多強(qiáng)大的附屬代碼信息,例如json_serializable等庫
所以,也能利用這點(diǎn)自定義類注解,獲取到對(duì)應(yīng)的整個(gè)類的代碼信息,在對(duì)應(yīng)附屬的xx.g.dart文件中,將獲取的代碼內(nèi)容轉(zhuǎn)換成字符串,然后直接將xx.g.dart文件的代碼字符串信息,展示到界面就行了
優(yōu)點(diǎn)
可以通過生成命令,全自動(dòng)的生成代碼,甚至將整個(gè)預(yù)覽 demo 的映射表都可以自動(dòng)配置完成
可以規(guī)范的通過注解配置多個(gè)參數(shù)
缺點(diǎn)
因?yàn)閎uild_runner需要解析整個(gè) ast 語法樹,一旦項(xiàng)目很大之后,解析生成的時(shí)間會(huì)非常非常的長(zhǎng)!
因?yàn)楝F(xiàn)在很多的這類庫都是依賴build_runner,所以跑自動(dòng)生成命令,會(huì)導(dǎo)致巨多xx.g.dart文件被改動(dòng),極大的增加 cr 工作量
資源文件方案
這應(yīng)該最常用的一種方案
在pubspec.yaml中的assets中定義下我們代碼文件路徑
flutter: assets: - lib/widgets/show/
然后用 loadString 獲取文件內(nèi)容
final code = await rootBundle.loadString('lib/widgets/show/custome_dialog_animation.dart');
優(yōu)點(diǎn)
侵入性非常低,不會(huì)像build_runnner方案那樣影響到其它模塊
便于維護(hù),如果 demo 預(yù)覽代碼被改變了,打包的時(shí)候,資源文件也會(huì)生成對(duì)應(yīng)改變后的代碼文件
缺點(diǎn)
使用麻煩,使用的時(shí)候需要傳入具體的文件路徑,才能找到想要的代碼資源文件
需要反復(fù)的在pubspec.yaml中的assets里面定義文件路徑
資源文件方案優(yōu)化
上面的三種方案各有優(yōu)缺點(diǎn),明確當(dāng)前的訴求
目前是想寫個(gè)簡(jiǎn)單的,通用的,僅在 Flutter 中實(shí)現(xiàn)代碼預(yù)覽方案
要求使用簡(jiǎn)單,高效
維護(hù)簡(jiǎn)單,多人開發(fā)的時(shí)候不會(huì)有很大成本
FlutterUnit 方案:實(shí)現(xiàn)起來成本較大,且多人開發(fā)對(duì)單個(gè) db 文件的維護(hù)很可能會(huì)有點(diǎn)問題,例如:更新代碼的時(shí)候,db 文件忘記更新
build_runner 方案:生成時(shí)間是個(gè)問題,還有很對(duì)其他類型xx.g.dart文件產(chǎn)生影響也比較麻煩
資源文件方案:整體是符合預(yù)期的,但是使用時(shí)候,需要傳入路徑和pubspec.yaml中反復(fù)定義文件路徑,這是倆個(gè)很大痛點(diǎn)
結(jié)合實(shí)現(xiàn)成本和訴求,選擇資源文件方案,下面對(duì)其痛點(diǎn)進(jìn)行優(yōu)化
使用優(yōu)化
Flutter 的編譯產(chǎn)物中,有個(gè)相當(dāng)有用的文件:AssetManifest.json
AssetManifest.json 文件里面,有所有的資源文件的路徑,然后就簡(jiǎn)單了,我們只需要讀取該文件內(nèi)容
final manifestContent = await rootBundle.loadString('AssetManifest.json');
獲取到所有的路徑之后,再結(jié)合傳入的類名,讀取所有路徑的文件內(nèi)容,然后和傳入的類名做正則匹配就行了
稍微優(yōu)化
將傳入的類名,轉(zhuǎn)換為下劃線名稱和所有路徑名稱做匹配,如果能匹配上,再進(jìn)行內(nèi)容匹配,匹配成功后就返回該文件的代碼內(nèi)容
如果上述匹配失敗,就進(jìn)行兜底的全量匹配
優(yōu)化前
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'); } }
優(yōu)化后
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'); } }
一般來說,我是統(tǒng)一配置預(yù)覽 demo 和 className,這樣比較好對(duì)照
路徑定義優(yōu)化
本來是想在pubspec.yaml的assets里面直接寫通配符定義全路徑,然后悲劇了,它不支持這種寫法
flutter: assets: - lib/widgets/**/*.dart
GG,只能想其他辦法了,想了很多方法都不行,只能從外部入手,用 idea 插件的形式,實(shí)現(xiàn)自動(dòng)化掃描生成路徑
安裝:Plugins 中搜索Flutter Code Helper
pugspec.yaml 中定義下需要自動(dòng)生成文件夾的目錄,文件夾隨便套娃,會(huì)自動(dòng)幫你遞歸在 assets 下生成
不需要自動(dòng)生成,可:不寫該配置,或者配置空數(shù)組(auto_folder: [])
code_helper: auto_folder: [ "assets/", "lib/widgets/" ]
Flutter Web 中的問題
魔幻的 runtimeType
flutter web 的 release 模式中
dart2js 會(huì)壓縮 JS,這樣會(huì)使得類型名被改變
例如:dart 中的TestWidgetFunction類的 runtimeType,可能會(huì)變成minified:Ah,而不是TestWidgetFunction!
為啥需要壓縮呢?壓縮名稱可以使得編譯器將 JavaScript 體積縮小 3 倍 +;精確等效語義和性能 / 代碼大小之間的權(quán)衡,Dart 明顯是選擇了后者
這種情況只會(huì)在 Flutter Web 的 release 模式下發(fā)生,其他平臺(tái)和 Flutter web 的 Debug | Profile 模式都不會(huì)有這種問題;所以說Xxx.runtimeType.toString,并不一定會(huì)得到預(yù)期內(nèi)的數(shù)據(jù)。。。
解決思路
將壓縮類型minified:Ah恢復(fù)成Test
將獲取的Test字符串使用相同算法壓縮成minified:Ah
如有知道如何實(shí)現(xiàn)的,務(wù)必告訴鄙人
下面從壓縮級(jí)別調(diào)整的角度,探究是否可解決該問題
dart2js 壓縮說明
注:flutter build web 默認(rèn)的是 O4 優(yōu)化級(jí)別
O0: 禁用許多優(yōu)化。
O1: 啟用默認(rèn)優(yōu)化 (僅是 dart2js 該命令的默認(rèn)級(jí)別)
O2: 在 O1 優(yōu)化基礎(chǔ)上,尊重語言語義且對(duì)所有程序安全的其他優(yōu)化(例如縮?。?/p>
備注:使用 - O2, 使用開發(fā) JavaScript 編譯器編譯時(shí),類型的字符串表示不再與 Dart VM 中的字符串表示相同
O3: 在 O2 優(yōu)化基礎(chǔ)上,并省略隱式類型檢查。
注意:省略類型檢查可能會(huì)導(dǎo)致應(yīng)用程序因類型錯(cuò)誤而崩潰
O4: 在 O3 優(yōu)化基礎(chǔ)上,啟用更積極的優(yōu)化
注意:O4 優(yōu)化容易受到輸入數(shù)據(jù)變化的影響,在依賴 O4 之前,需測(cè)試用戶輸入中的邊緣情況
下面是 flutter 新建項(xiàng)目,未做任何改動(dòng),不同壓縮級(jí)別的 js 產(chǎn)物體積
# 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
總結(jié)
預(yù)期用法
為什么想使用對(duì)象?因?yàn)楫?dāng)對(duì)象名稱改變時(shí),對(duì)應(yīng)使用的地方,可以便捷觀察到需要改變
可以使用傳入的對(duì)象實(shí)例,在內(nèi)部使用 runtimeType 獲取類型名,再進(jìn)行相關(guān)匹配
CodePreview(code: Test());
但是
綜上可知,使用flutter build web --dart2js-optimization O1編譯的 flutter web release 產(chǎn)物,能夠使得 runtimeType 的語義和 Dart VM 中字符串保持一致
但是該壓縮級(jí)別下的,js 體積過于夸張,務(wù)必會(huì)對(duì)加載速度產(chǎn)生極大影響,可想而知,在復(fù)雜項(xiàng)目中的體積增漲肯定更加離譜
對(duì)于想要用法更加簡(jiǎn)單,使用低級(jí)別壓縮命令打包的想法需要舍棄
用法不得已做妥協(xié)
CodePreview(className: "Test");
這是個(gè)讓我非常糾結(jié)的思路歷程
最后
到這里也結(jié)束了,自我感覺,對(duì)大家應(yīng)該能有一些幫助
一般來說,大部分團(tuán)隊(duì),都會(huì)有個(gè)自己的內(nèi)部組件庫,因?yàn)?Flutter 強(qiáng)大的跨平臺(tái)特性,所以就能很輕松的發(fā)布到 web 平臺(tái),可以方便的體驗(yàn)各種組件的效果,結(jié)合文章中的代碼預(yù)覽方案,就可以更加快速的上手各種組件用法了~
審核編輯:劉清
-
Rayc
+關(guān)注
關(guān)注
0文章
2瀏覽量
6012 -
flutter
+關(guān)注
關(guān)注
0文章
13瀏覽量
432
原文標(biāo)題:Flutter如何將代碼顯示到界面上
文章出處:【微信號(hào):OSC開源社區(qū),微信公眾號(hào):OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論