前言
如何優(yōu)雅的將項(xiàng)目中的代碼,亦或是你的 demo 代碼展示到界面上?本文對(duì)使用簡(jiǎn)單、便于維護(hù)且通用的解決方案,進(jìn)行相關(guān)的對(duì)比和探究
為了節(jié)省大家的時(shí)間,把最終解決方案的相關(guān)接入和用法寫(xiě)在前面
預(yù)覽代碼
快速開(kāi)始
接入:pub,github
dependencies: code_preview: ^0.1.5
用法:CodePreview,提供需要預(yù)覽的 className,可自動(dòng)匹配該類對(duì)應(yīng)的代碼文件
本來(lái)想把寫(xiě)法簡(jiǎn)化成傳入對(duì)象,但是因?yàn)橐恍┰驘o(wú)奈放棄,改成了className
具體可以參考下面Flutter Web中的問(wèn)題模塊的說(shuō)明
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)生成,可:不寫(xiě)該配置,或者配置空數(shù)組(auto_folder: [])
code_helper: auto_folder: [ "assets/", "lib/widgets/" ]
說(shuō)明下:上面的插件是基于 RayC 的 FlutterAssetsGenerator 插件項(xiàng)目改的
看了下 RayC 的插件代碼和相關(guān)功能,和我預(yù)想的上面功能實(shí)現(xiàn)有一定出入,改動(dòng)起來(lái)變動(dòng)較大
想試下插件項(xiàng)目的各種新配置,直接拉到最新
后期如果想到需要什么功能,方便隨時(shí)添加
所以沒(méi)向其插件里面提 pr,就單獨(dú)新開(kāi)了個(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
一般來(lái)說(shuō),可以根據(jù)注釋獲取的數(shù)據(jù),結(jié)合codeWidget嵌套來(lái)自定義符合要求的布局
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 代碼
將他們的文本信息都掃描出來(lái),然后解析上面的注釋等相關(guān)信息,分類存儲(chǔ)到數(shù)據(jù)庫(kù)中,最后生成 db 文件
映射表,宿主可以通過(guò) db 中的組件類名,從這里拿到 demo 效果實(shí)例
總結(jié)
整套流程看下來(lái),實(shí)現(xiàn)起來(lái)的工作量還是有點(diǎn)大的
db 輔助工具的編寫(xiě)
文本注釋相關(guān)解析規(guī)則
如何便捷的維護(hù) db 文件(輔助工具是否支持,生成后自動(dòng)覆蓋宿主 db 文件)
不同平臺(tái) db 文件的讀取和相關(guān)適配
優(yōu)點(diǎn)
因?yàn)閽呙韫ぞ卟灰蕾?Flutter 相關(guān)庫(kù),預(yù)覽方案可以快速的移植到其它編程語(yǔ)言(compose,SwiftUI 等)
具備高度自定義,因?yàn)槭峭耆?dú)立的第三方掃描工具,可以隨性所欲的定制化
缺點(diǎn)
最明顯的缺點(diǎn),應(yīng)該就是稍微改下 demo 代碼,就需要三方工具重新生成 db 文件(如果三方工具實(shí)現(xiàn)的是 cli 工具,可以將掃描生成命令和 push 等命令集成一起,應(yīng)該可以比較好的避免該問(wèn)題)
build_runner 方案
build_runner 是個(gè)強(qiáng)大代碼自動(dòng)生成工具,根據(jù) ast 語(yǔ)法樹(shù) + 自定義注解信息,可以生成很多強(qiáng)大的附屬代碼信息,例如json_serializable等庫(kù)
所以,也能利用這點(diǎn)自定義類注解,獲取到對(duì)應(yīng)的整個(gè)類的代碼信息,在對(duì)應(yīng)附屬的xx.g.dart文件中,將獲取的代碼內(nèi)容轉(zhuǎn)換成字符串,然后直接將xx.g.dart文件的代碼字符串信息,展示到界面就行了
優(yōu)點(diǎn)
可以通過(guò)生成命令,全自動(dòng)的生成代碼,甚至將整個(gè)預(yù)覽 demo 的映射表都可以自動(dòng)配置完成
可以規(guī)范的通過(guò)注解配置多個(gè)參數(shù)
缺點(diǎn)
因?yàn)閎uild_runner需要解析整個(gè) ast 語(yǔ)法樹(shù),一旦項(xiàng)目很大之后,解析生成的時(shí)間會(huì)非常非常的長(zhǎng)!
因?yàn)楝F(xiàn)在很多的這類庫(kù)都是依賴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)前的訴求
目前是想寫(xiě)個(gè)簡(jiǎn)單的,通用的,僅在 Flutter 中實(shí)現(xiàn)代碼預(yù)覽方案
要求使用簡(jiǎn)單,高效
維護(hù)簡(jiǎn)單,多人開(kāi)發(fā)的時(shí)候不會(huì)有很大成本
FlutterUnit 方案:實(shí)現(xiàn)起來(lái)成本較大,且多人開(kāi)發(fā)對(duì)單個(gè) db 文件的維護(hù)很可能會(huì)有點(diǎn)問(wèn)題,例如:更新代碼的時(shí)候,db 文件忘記更新
build_runner 方案:生成時(shí)間是個(gè)問(wèn)題,還有很對(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'); } }
一般來(lái)說(shuō),我是統(tǒng)一配置預(yù)覽 demo 和 className,這樣比較好對(duì)照
路徑定義優(yōu)化
本來(lái)是想在pubspec.yaml的assets里面直接寫(xiě)通配符定義全路徑,然后悲劇了,它不支持這種寫(xiě)法
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)生成,可:不寫(xiě)該配置,或者配置空數(shù)組(auto_folder: [])
code_helper: auto_folder: [ "assets/", "lib/widgets/" ]
Flutter Web 中的問(wèn)題
魔幻的 runtimeType
flutter web 的 release 模式中
dart2js 會(huì)壓縮 JS,這樣會(huì)使得類型名被改變
例如:dart 中的TestWidgetFunction類的 runtimeType,可能會(huì)變成minified:Ah,而不是TestWidgetFunction!
為啥需要壓縮呢?壓縮名稱可以使得編譯器將 JavaScript 體積縮小 3 倍 +;精確等效語(yǔ)義和性能 / 代碼大小之間的權(quán)衡,Dart 明顯是選擇了后者
這種情況只會(huì)在 Flutter Web 的 release 模式下發(fā)生,其他平臺(tái)和 Flutter web 的 Debug | Profile 模式都不會(huì)有這種問(wèn)題;所以說(shuō)Xxx.runtimeType.toString,并不一定會(huì)得到預(yù)期內(nèi)的數(shù)據(jù)。。。
解決思路
將壓縮類型minified:Ah恢復(fù)成Test
將獲取的Test字符串使用相同算法壓縮成minified:Ah
如有知道如何實(shí)現(xiàn)的,務(wù)必告訴鄙人
下面從壓縮級(jí)別調(diào)整的角度,探究是否可解決該問(wèn)題
dart2js 壓縮說(shuō)明
注: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ǔ)上,尊重語(yǔ)言語(yǔ)義且對(duì)所有程序安全的其他優(yōu)化(例如縮?。?/p>
備注:使用 - O2, 使用開(kāi)發(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 的語(yǔ)義和 Dart VM 中字符串保持一致
但是該壓縮級(jí)別下的,js 體積過(guò)于夸張,務(wù)必會(huì)對(duì)加載速度產(chǎn)生極大影響,可想而知,在復(fù)雜項(xiàng)目中的體積增漲肯定更加離譜
對(duì)于想要用法更加簡(jiǎn)單,使用低級(jí)別壓縮命令打包的想法需要舍棄
用法不得已做妥協(xié)
CodePreview(className: "Test");
這是個(gè)讓我非常糾結(jié)的思路歷程
最后
到這里也結(jié)束了,自我感覺(jué),對(duì)大家應(yīng)該能有一些幫助
一般來(lái)說(shuō),大部分團(tuán)隊(duì),都會(huì)有個(gè)自己的內(nèi)部組件庫(kù),因?yàn)?Flutter 強(qiáng)大的跨平臺(tái)特性,所以就能很輕松的發(fā)布到 web 平臺(tái),可以方便的體驗(yàn)各種組件的效果,結(jié)合文章中的代碼預(yù)覽方案,就可以更加快速的上手各種組件用法了~
審核編輯:劉清
-
Rayc
+關(guān)注
關(guān)注
0文章
2瀏覽量
6106 -
flutter
+關(guān)注
關(guān)注
0文章
13瀏覽量
572
原文標(biāo)題:Flutter如何將代碼顯示到界面上
文章出處:【微信號(hào):OSC開(kāi)源社區(qū),微信公眾號(hào):OSC開(kāi)源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
如何將RT-Thread移植到NXP MCUXPressoIDE上

評(píng)論