关于在Flutter Web中加载html(持续更新中……)

??最近碰到一个需求,需要在Flutter Web项目中加载html。在手机端我们可以使用webview_plugin插件加载,但是这个插件在web上面是无效的。那么,在Flutter Web中,该如何加载html呢?Flutter为我们提供了一个专门用于web的控件:HtmlElementView。(这里说点题外话,HtmlElementView的注释文档里面有写,构建html是昂贵操作,如果有等效的Flutter实现可以替换,那么请尽量不要这么搞。)

HtmlElementView的基本使用规则

??使用这个控件必须要注意,使用之前需要注册!!!
??可以在initState()的时候注册,当然你也可以在runApp()的时候就全局注册好(个人不建议):

1
2
3
4
import 'dart:ui' as ui;
ui.platformViewRegistry.registerViewFactory(
        'hello-world-html',
        (int viewId) => HtmlElement();

??ui.platformViewRegistry会报静态错误,不用管,完全不影响你代码跑起来。解释下registerViewFactory()方法中传的两个参数:第一个个人理解是给你需要插入的html片段取一个id名称,第二个参数则是对应的真正用来加载html的元素。这里的HtmlElement()是一个基类,实际书写的时候应该传入IFrameElementHtmlHtmlElementMediaElementBodyElement等等你实际需要的元素。
??使用HtmlElementView时,只需要将已注册元素对应的id传进去就可以了:

1
2
3
HtmlElementView(
    viewType: 'hello-world-html',
    )

加载URL

??加载URL可以使用IFrameElement(url请写全了,不要漏掉https):

1
2
3
4
5
ui.platformViewRegistry.registerViewFactory(
        'hello-world-html',
        (int viewId) => IFrameElement()
          ..style.border = 'none'
          ..src = 'https://www.baidu.com');

加载本地html文件

1
2
3
4
5
ui.platformViewRegistry.registerViewFactory(
        'hello-world-html',
        (int viewId) => IFrameElement()
          ..style.border = 'none'
          ..src = '/assets/test2.html');

加载html string

  • 加载比较复杂的混合型html

??如果是各种标签混合的html,建议使用HtmlHtmlElement

1
2
3
4
5
ui.platformViewRegistry.registerViewFactory(
        'hello-world-html',
        (int viewId) => HtmlHtmlElement()
          ..style.border = 'none'
          ..setInnerHtml(html));

??这里要注意有坑,因为实际项目中使用到的html,含有之类的标签是很正常的,但是如果你按照上面的方式加载,图片、超链接之类的都是没法显示的,因为默认的HtmlHtmlElement是把这些标签禁用掉的(估计是为了性能吧,毕竟开篇就说了,Flutter其实不希望你直接加载html标签的),你需要手动开启允许加载复杂标签。具体做法是传入一个validator参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

??建议使用轻量型的MediaElementBodyElementFormElementMenuElement等等。