Flutter 如何將代碼顯示到界面上

来源:https://www.cnblogs.com/xdd666/archive/2023/05/05/17373084.html
-Advertisement-
Play Games

如何優雅的將項目中的代碼,亦或是你的demo代碼展示到界面上?本文對使用簡單、便於維護且通用的解決方案,進行相關的對比和探究 ...


前言

如何優雅的將項目中的代碼,亦或是你的demo代碼展示到界面上?本文對使用簡單、便於維護且通用的解決方案,進行相關的對比和探究

為了節省大家的時間,把最終解決方案的相關接入和用法寫在前面

預覽代碼

快速開始

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

image-20230429215042820

配置代碼文件

因為原理是遍歷資源文件,所以必須將需要展示的代碼文件或者其文件夾路徑,定義在assets下,這步操作,為大家提供了一個自動化的插件解決

強烈建議需要展示到界面的代碼,都放在統一的文件夾里管理

  • 展示界面的代碼需要在pugspec.yaml中的assets定義

image-20230422224011359

如果代碼預覽的文件夾,分級複雜,每次都需要定義路徑實在麻煩

提供一個插件:Flutter Code Helper

  • 安裝:Plugins中搜索Flutter Code Helper

image-20230422225244651

  • pugspec.yaml中定義下需要自動生成文件夾的路徑,文件夾隨便套娃,會自動幫你遞歸在assets下生成
    • 不需要自動生成,可:不寫該配置,或者配置空數組(auto_folder: [])
code_helper:
  auto_folder: [ "assets/", "lib/widgets/" ]

Apr-09-2023 22-33-42

說明下:上面的插件是基於RayCFlutterAssetsGenerator插件項目改的

  • 看了下RayC的插件代碼和相關功能,和我預想的上面功能實現有一定出入,改動起來變動較大
  • 想試下插件項目的各種新配置,直接拉到最新
  • 後期如果想到需要什麼功能,方便隨時添加

所以沒向其插件裡面提pr,就單獨新開了個插件項目

高級使用

主題

提供倆種代碼樣式主題

  • 日間模式
CodePreview.config = CodePreviewConfig(codeTheme: CodeTheme.light);

image-20230429215716043

  • 夜間模式
CodePreview.config = CodePreviewConfig(codeTheme: CodeTheme.dark);

image-20230429215545723

註釋解析

  • 你可以使用如下的格式,在類上添加註釋
    • 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文件

image-20230429172832212

  • 映射表,宿主可以通過db中的組件類名,從這裡拿到demo效果實例

image-20230429175714400

總結

整套流程看下來,實現起來的工作量還是有點大的

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

image-20230429205530817

優點

  • 侵入性非常低,不會像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-20230429170007279

路徑定義優化

本來是想在pubspec.yamlassets裡面直接寫通配符定義全路徑,然後悲劇了,它不支持這種寫法

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

GG,只能想其他辦法了,想了很多方法都不行,只能從外部入手,用idea插件的形式,實現自動化掃描生成路徑

  • 安裝:Plugins中搜索Flutter Code Helper

image-20230422225244651

  • pugspec.yaml中定義下需要自動生成文件夾的目錄,文件夾隨便套娃,會自動幫你遞歸在assets下生成
    • 不需要自動生成,可:不寫該配置,或者配置空數組(auto_folder: [])
code_helper:
  auto_folder: [ "assets/", "lib/widgets/" ]

Apr-09-2023 22-33-42

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平臺,可以方便的體驗各種組件的效果,結合文章中的代碼預覽方案,就可以更加快速的上手各種組件用法了~

好了,下次再見了,靚仔們!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文介紹將Windows電腦中的Administrator、網路、回收站等系統自帶桌面圖標取消顯示或恢復顯示的方法。 在Windows10電腦中,一般在桌面上預設會顯示如下所示的一些系統自帶圖標。 然而,在上述這些圖標中,有一些我們可能相對而言使用的頻率比較低,比如網路圖標,以及上圖中最上面的Adm ...
  • 本文源碼部分基於內核 5.4 版本討論 在經過上篇文章 《從內核源碼看 slab 記憶體池的創建初始化流程》 的介紹之後,我們最終得到下麵這幅 slab cache 的完整架構圖: 本文筆者將帶大家繼續從內核源碼的角度繼續拆解 slab cache 的實現細節,接下來筆者會基於上面這幅 slab ca ...
  • 哈嘍大家好我是鹹魚,在《Linux 記憶體管理 pt.1》中我們學習了什麼是物理記憶體、虛擬記憶體,瞭解了記憶體映射、缺頁異常等內容 那麼今天我們來接著學習 Linux 記憶體管理中的多級頁表和大頁 多級頁表&大頁 在《Linux 記憶體管理 pt.1》中我們知道了內核為每個進程都維護了一張頁表,這張頁表用來記 ...
  • 在MySQL中,這幾個都是統計操作,很多人在使用的時候,都使用的是count(1),這有沒有問題?使用正確?達到了統計效果? 我們從效果和效率兩方面來分析下 執行效果 count(*) 包括了所有的列,在統計時不會忽略列值為null的數據count(1) 用1表示代碼行,在統計時不會忽略列值為nul ...
  • 4月20日,袋鼠雲成功舉行了以“數實融合,韌性生長”為主題的2023春季生長大會。會上重磅發佈了袋鼠雲生態伙伴計劃——“飛躍計劃2.0”,從商機、產品、聯合方案及數據業務服務層面,與合作伙伴強強聯手,共同打造數字化生態,同時在聯合營銷、渠道政策、賦能培訓、產品開放、技術服務、交付實施等方面全面升級夥 ...
  • 摘要:本文主要介紹GaussDB(DWS)網路流控能力,並對其管控效果進行驗證。 本文分享自華為雲社區《GaussDB(DWS)網路流控與管控效果》,作者:門前一棵葡萄樹。 上一篇博文GaussDB(DWS)網路調度與隔離管控能力,我們詳細介紹了GaussDB網路調度邏輯,並簡單介紹瞭如何應用網路隔 ...
  • 0. 前情提要 系統的某個用來上報數據的介面存在死鎖的問題。這個介面內部對多張表進行了Update操作,執行順序為A表、B表、C表、D表、A表。死鎖發生的SQL,一條是第一次更新A表的SQL,另一條是第二次更新A表的SQL。整個更新都處在一個事務內,理論上講,只要第一個Session開始執行事務,第 ...
  • Redis事務(Transaction)通過將多個Redis操作封裝為一個原子性的操作序列,確保在事務執行過程中,不會受到其他客戶端的干擾。從而在保證數據一致性的同時,協調併發,提高數據操作的效率和性能 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...