關於在Flutter Web中載入html(持續更新中......)

吉原拉麵發表於2020-03-17

  最近碰到一個需求,需要在Flutter Web專案中載入html。在手機端我們可以使用webview_plugin外掛載入,但是這個外掛在web上面是無效的。那麼,在Flutter Web中,該如何載入html呢?Flutter為我們提供了一個專門用於web的控制元件:HtmlElementView。(這裡說點題外話,HtmlElementView的註釋文件裡面有寫,構建html是昂貴操作,如果有等效的Flutter實現可以替換,那麼請儘量不要這麼搞。)

HtmlElementView的基本使用規則

  使用這個控制元件必須要注意,使用之前需要註冊!!!
  可以在initState()的時候註冊,當然你也可以在runApp()的時候就全域性註冊好(個人不建議):

import 'dart:ui' as ui;
ui.platformViewRegistry.registerViewFactory(
        'hello-world-html',
        (int viewId) => HtmlElement();
複製程式碼

  ui.platformViewRegistry會報靜態錯誤,不用管,完全不影響你程式碼跑起來。解釋下registerViewFactory()方法中傳的兩個引數:第一個個人理解是給你需要插入的html片段取一個id名稱,第二個引數則是對應的真正用來載入html的元素。這裡的HtmlElement()是一個基類,實際書寫的時候應該傳入IFrameElementHtmlHtmlElementMediaElementBodyElement等等你實際需要的元素。
  使用HtmlElementView時,只需要將已註冊元素對應的id傳進去就可以了:

HtmlElementView(
    viewType: 'hello-world-html',
    )
複製程式碼

載入URL

  載入URL可以使用IFrameElement(url請寫全了,不要漏掉https):

ui.platformViewRegistry.registerViewFactory(
        'hello-world-html',
        (int viewId) => IFrameElement()
          ..style.border = 'none'
          ..src = 'https://www.baidu.com');
複製程式碼

載入本地html檔案

ui.platformViewRegistry.registerViewFactory(
        'hello-world-html',
        (int viewId) => IFrameElement()
          ..style.border = 'none'
          ..src = '/assets/test2.html');
複製程式碼

載入html string

  • 載入比較複雜的混合型html(篇幅比較短的)

  如果是各種標籤混合的html,可以使用HtmlHtmlElement

ui.platformViewRegistry.registerViewFactory(
        'hello-world-html',
        (int viewId) => HtmlHtmlElement()
          ..style.border = 'none'
          ..setInnerHtml(html));
複製程式碼

  這裡要注意有坑,因為實際專案中使用到的html,含有<img><a href>之類的標籤是很正常的,但是如果你按照上面的方式載入,圖片、超連結之類的都是沒法顯示的,因為預設的HtmlHtmlElement是把這些標籤禁用掉的(估計是為了效能吧,畢竟開篇就說了,Flutter其實不希望你直接載入html標籤的),你需要手動開啟允許載入複雜標籤。具體做法是傳入一個validator引數:

final NodeValidator _validator = NodeValidatorBuilder.common()
    ..allowElement('img', attributes: ['src'], uriPolicy: _AllowUriPolicy())
    ..allowElement('a', attributes: ['href'], uriPolicy: _AllowUriPolicy());
    
ui.platformViewRegistry.registerViewFactory(
        'hello-world-html',
        (int viewId) => HtmlHtmlElement()
          ..style.border = 'none'
          ..setInnerHtml(html,validator: _validator));
         
class _AllowUriPolicy implements UriPolicy {
  @override
  bool allowsUri(String uri) {
    return true;
  }
}
複製程式碼
  • 載入比較複雜的混合型html(篇幅比較長的)

  如果是長篇大論的html,如果你用HtmlHtmlElement載入,就會發現頁面無法滾動。嘗試過與ScrollView組合展示,比較麻煩,因為如果有圖片一類的資原始檔的話,HtmlElementView的高度很難計算,反正我沒搞定,如果有大佬有方案歡迎下方留言指導。   嘗試後發現還是需要利用萬能的IFrameElement,使用srcdoc的方式載入:

ui.platformViewRegistry.registerViewFactory(
        'hello-world-html',
        (int viewId) => IFrameElement()
          ..style.border = 'none'
          ..srcdoc = content);
複製程式碼
  • 載入單個標籤的html

  建議使用輕量型的MediaElementBodyElementFormElementMenuElement等等。

相關文章