一個易遷移、相容性高的 Flutter 富文字方案

iOS面試簡歷專員發表於2020-04-23

背景

在閒魚訊息體系中,富文字在 UI 側佔了非常大的比重。最近訊息部分在整體 Flutter 化,如何解決 Flutter 側富文字問題,成為了專案早期的風險點。

在 Native 中,訊息使用了 HTML 協議來承載富文字的解析與展示,由於訊息的歷史資料有落庫的特性,我們必須在 Flutter 側相容這種協議。對於 Flutter,我們是否可以在相容的基礎上,進行能力的擴充與完善?

當前閒魚也在升級 Flutter 1.12,所以我們不光要在當前版本支援圖文混排,也需要快速遷移到高版本的系統方案。因此我們需要找到一個相容性高、易遷移的富文字方案。

行業現狀

行業內,對於舊版的 RichText (Flutter 1.7.3 之前)已有了解決方案,詳見玄川:《如何低成本實現Flutter 富文字,看這一篇就夠了!》。但這裡並沒有對富文字的整個鏈路的解決思路,且 Flutter 自身的 RichText 也在隨著版本迭代進行演進,我們需要一套完整的演進方案。

事實上,Flutter 1.7.3 開始的 RichText 解決了我們的很多麻煩,它是怎麼實現的呢?和舊版的實現有什麼區別呢?帶著問題,我們先來分析它的實現原理。

RichText 圖文混排原理

Flutter 1.7.3 開始,RichText 不再繼承自 LeafRenderObjectWidget,而是繼承自 MultiChildRenderObjectWidget,從這就很容易看出,RichText 將是一個佈局控制元件,內部可以有多個子控制元件。

建立過程:
一個易遷移、相容性高的 Flutter 富文字方案

如上圖,我們傳給 RichText 的 text 引數為 InlineSpan,TextSpan、WidgetSpan都是其子類。

  1. RichText 初始化過程中會將 text 中的所有 WidgetSpan 遞迴篩選出來,傳遞給父類 MultiChildRenderObjectWidget。
  2. 建立 MultiChildRenderObjectElement,接著 RichText 會透過 createRenderObject,生成 RenderParagraph。
  3. RenderParagraph 初始化過程中會建立 TextPainter,這個是繪製的核心,這裡將會進行 layout、paint 和事件分發操作;然後遞迴篩選 PlaceholderSpan (其實還是 WidgetSpan)。
渲染過程:
一個易遷移、相容性高的 Flutter 富文字方案

上圖為 RenderParagraph 內的 performLayout 函式。

  1. 如上圖 RenderParagraph 執行 performLayout 。首先 _layoutChildren:為子控制元件佈局,目的為獲取子控制元件大小,如果沒有子控制元件則直接 return。這裡所說的子控制元件就是 WidgetSpan。
  2. 第二步 _layoutTextWithConstraints,就是執行 _textPainter 的 layout 方法,這裡會讓 text(InlineSpan)進行 build,此時會按照它的樹形結構遍歷執行。
    1. TextWidget build 時會將自身的 text(這是真實的字串)addText 給 builder。
    2. WidgetSpan build 時會將自身控制元件的 PlaceholderDimensions 資訊,addPlaceholder 給 builder。這裡其實就是新增佔位符,佔位符將與控制元件同大小。
    3. 緊接著 _paragraph 會進行一次佈局,然後獲取各個佔位符位置儲存下來。
  3. Paint 過程會先將帶著佔位符的文字繪製完成,然後遍歷子控制元件按照 2-3 步驟中獲得的佔位符位置,設定偏移。

概括來說,新版本對比舊版本,底層多了個 _addPlaceholder 能力,用來佔位混排的 Widget,並獲取位置資訊。

設計思路

我們以 HTML 協議為抓手,不光可以解決普通 HTML 字串的解析與渲染,也可以對使用者傳送的帶閒魚自定義 emoji 的字串進行能力的擴充。下圖為大致的設計思路:

一個易遷移、相容性高的 Flutter 富文字方案

當前訊息展示分為兩種場景,一種為帶有閒魚自定義 emoji 表情的字串:

你好[微笑],你的寶貝不錯哦[呲牙],包郵嗎?[壞笑][壞笑]

另一種為簡單的 HTML 字串:

"<font color="#888888">交易全程在閒魚,</font><strong><font color="#F54444">你敢買,我敢賠!</font></strong><font color="#888888">若遇欺詐造成</font><strong><font color="#F54444">錢貨兩失,可獲賠</font></strong><strong><font color="#F54444">最高5000元</font></strong>"

當然,還有最普通的純文字。

對於這三種字串,服務端並沒有用型別來給我們區分,客戶端拿到的都為字串。端側該如何處理且高效展示呢?

過程設計成這樣:

  1. 首先對於確定為純文字的控制元件,直接使用單 TextSpan 的 RichText,免去 Text 的封裝。
  2. 使用  RegExp(r'\[[^\]\[]+\]') 匹配  [微笑] 等 emoji 佔位符,替換為  <img src=003_微笑.png width=22.400000 height=22.400000/>
  3. 取最後的 HTMLString ,使用  html | Dart Package ,進行 HTML 解析,生成 HTML Node Tree
  4. 遞迴 HTML Node Tree
    1. 文字標籤對映為 TextSpan
    2. 圖片標籤對映為 FDImageSpan;Flutter 升級後將其替換為 WidgetSpan,其 child 設定為 Image Widget
    3. 連結標籤對映為 TextSpan,定義 GestureRecognizer 相應手勢

流程上,先將閒魚自定義 emoji 佔位符轉為 HTML 元素,接著統一處理 HTML 字串。然後將 HTML 字串統一轉為富文字。設計上,分為兩層:資料解析層、渲染層。

一個易遷移、相容性高的 Flutter 富文字方案
image

如上圖,有了前面原生 Flutter 圖文混排支撐,我們在低版本可以仿照實現,低版本 RichText 繼承自 LeafRenderObjectWidget,我們把 RichText 與其他 Widget 組成新的 MultiChildRenderObjectWidget,透過佔位符正常渲染文字,之後獲取佔位符位置,設定對應 Widget 的位置。

Flutter SDK 升級過程中如何保持業務方無感知?先看下圖:

一個易遷移、相容性高的 Flutter 富文字方案
image

對比發現,在 TextSpan 樹中,我們繼承自 TextSpan 的自定義 FDImageSpan,實際上可以直接對應到原生的 WidgetSpan,這裡我們可以在 HTML Node Tree 對映到 TextSpan Tree 的過程中直接修改。而 FDRichText build 裡,我們可以直接返回系統 RichText。這樣的改動,對於使用方可以做到無感知。

效果
一個易遷移、相容性高的 Flutter 富文字方案
image

上圖中是一種最為簡單和常見的系統訊息,為了突出安全警示,使用了較多的紅色字型。模組中定義的三個富文字,均可定製樣式。

一個易遷移、相容性高的 Flutter 富文字方案
image

上圖為涉及互動的富文字,買家可以點選藍色文字「那兒發貨」,然後買家會自動傳送「那兒發貨」給賣家,賣家會根據預設的問題自動回覆買家。點選會觸發 HTML 字串中的 href 自定義協議連結,客戶端會觸發 openURL 的操作,以此來實現互動。

一個易遷移、相容性高的 Flutter 富文字方案
image

這是普通使用者可以編輯傳送的富文字,豐富的閒魚自定義 emoji,穿插在文字中,不僅增加了聊天樂趣,也增強了使用者的表達。

未來

當前的展示部分僅僅是圖文混排,新版本中的富文字支援任意 Widget,可玩性更高,所以我們對 HTML 標籤描述可以進行擴充,這部分未來還需要持續探索。

由於篇幅有限,上文並沒有講述富文字編輯器。訊息中使用者輸入框也需支援閒魚自定義 emoji,當前版本的方案為直接使用佔位符(比如“[微笑]”),並不展示實際的圖片。我們回頭再看一下 HTML 協議,對於新版的 TextField,我們可以支援嗎?這就不僅僅侷限在自定義 emoji 裡了。

我們把目光轉向釋出和寶貝詳情:

一個易遷移、相容性高的 Flutter 富文字方案
image

我們後續可能會支援上圖中,釋出和寶貝詳情的富文字編輯和展示。對比兩個詳情頁,很明顯能看出,使用富文字的方式,在表達上更加富有衝擊力,買家閱讀起來能很容易抓住賣家想表達的關鍵資訊。

可見,未來在富文字的編輯、展示基礎能力統一之後,可以讓更多業務收益。

推薦?:

如果你想一起進階,不妨新增一下交流群 1012951431

面試題資料或者相關學習資料都在群檔案中 進群即可下載!

一個易遷移、相容性高的 Flutter 富文字方案

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69971523/viewspace-2687886/,如需轉載,請註明出處,否則將追究法律責任。

相關文章