pub package

一个功能丰富的跨平台Webview,使用 webview_flutter 用于移动端,以及 iframe 用于Web端。支持JS互操作。

画廊

移动端





Web






基本用法

1. 在你的有状态组件中创建一个 WebViewXController

late WebViewXController webviewController;

2. 在build方法中添加WebViewX widget,并设置 onWebViewCreated 回调,以便在webview初始化时获取控制器

WebViewX(
    initialContent: '<h2> Hello, world! </h2>',
    initialSourceType: SourceType.HTML,
    onWebViewCreated: (controller) => webviewController = controller,
    ...
    ... other options
);

重要!

如果你需要在webview顶部添加其他widget(例如,在Stack widget中),你必须将这些widget包裹在 WebViewAware widget中。
这在移动端什么也不做,但在Web端,它允许顶部的widget拦截手势。否则,这些widget可能无法点击,或者iframe会表现异常(意外刷新/重载 – 这是一个众所周知的问题)。

另外,如果你在webview顶部添加了widget,包裹了它们,然后发现iframe仍然意外重载,你应该检查是否有其他widget在不知情的情况下覆盖在上面,或者尝试包裹InkWell、GestureRecognizer或Button widget,看看是哪个导致了问题。

3. 通过控制器进行交互(运行 示例应用 查看一些用例)

webviewController.loadContent(
    'https://flutterdart.cn',
    SourceType.url,
);
webviewController.goBack();

webviewController.goForward();
...
...

功能

注意:有关 EmbeddedJsContent 等更详细的信息,请访问 utils 文件夹中各自的 .dart 文件。

  • Widget属性

功能 详情
String initialContent 初始webview内容
SourceType initialSourceType 初始webview内容类型(url, urlBypass 或 html
String? userAgent 用户代理
double width Widget宽度
double height Widget高度
Function(WebViewXController controller)? onWebViewCreated webview初始化后执行的回调
Set<EmbeddedJsContent> jsContent 一组EmbeddedJsContent,它是一个定义了一些JavaScript的对象,这些JavaScript将在页面加载后嵌入(查看示例应用)
Set<DartCallback> dartCallBacks 一组DartCallback,它是一个定义了一个Dart回调函数的对象,该函数将从JavaScript调用(查看示例应用)
bool ignoreAllGestures 一个布尔值,指定widget初始化后是否应忽略所有手势
JavascriptMode javascriptMode 这指定是否允许JavaScript执行(默认允许,要使用上述功能必须允许)
AutoMediaPlaybackPolicy initialMediaPlaybackPolicy 这指定在初始化时(即页面加载时)是否允许媒体内容自动播放
void Function(String src)? onPageStarted 页面开始加载时执行的回调(例如,在更改内容后)
void Function(String src)? onPageFinished 页面加载完成时执行的回调
NavigationDelegate? navigationDelegate 如果非空,当用户在webview 中点击某些内容时执行的回调(在Web端,目前仅适用于 SourceType.urlBypass
void Function(WebResourceError error)? onWebResourceError 加载资源时发生错误时执行的回调(Web端问题
WebSpecificParams webSpecificParams 这是一个包含Web端特定选项的对象。这些选项目前在移动端不可用(*暂时)
MobileSpecificParams mobileSpecificParams 这是一个包含移动端特定选项的对象。这些选项目前在Web端不可用(*暂时)

  • 控制器属性

功能 用法
加载允许iframe嵌入的URL webviewController.loadContent(URL, SourceType.URL)
加载不允许iframe嵌入的URL webviewController.loadContent(URL, SourceType.URL_BYPASS)
加载不允许iframe嵌入的URL,并带请求头 webviewController.loadContent(URL, SourceType.URL_BYPASS, headers: {'x-something': 'value'})
从字符串加载HTML webviewController.loadContent(HTML, SourceType.HTML)
从资源加载HTML webviewController.loadContent(HTML, SourceType.HTML, fromAssets: true)
检查是否可以回退历史记录 webviewController.canGoBack()
回退历史记录 webviewController.goBack()
检查是否可以前进历史记录 webviewController.canGoForward()
前进历史记录 webviewController.goForward()
重新加载当前内容 webviewController.reload()
检查是否忽略了所有手势 webviewController.ignoringAllGestures
设置忽略所有手势 webviewController.setIgnoreAllGestures(value)
评估“原始”JavaScript代码 webviewController.evalRawJavascript(JS)
在全局上下文中(“页面”)评估“原始”JavaScript代码 webviewController.evalRawJavascript(JS, inGlobalContext: true)
调用JS方法 webviewController.callJsMethod(METHOD_NAME, PARAMS_LIST)
检索webview的内容 webviewController.getContent()
获取X轴滚动位置 webviewController.getScrollX()
获取Y轴滚动位置 webviewController.getScrollY()
在X轴上按 x 滚动,在Y轴上按 y 滚动 webviewController.scrollBy(int x, int y)
精确滚动到位置 (x, y) webviewController.scrollTo(int x, int y)
检索内部页面标题 webviewController.getTitle()
清除缓存 webviewController.clearCache()

限制和注意事项

虽然这个包旨在将两全其美结合起来,但Web和移动端之间存在差异。

  • 运行和构建

    首先,这个包是在默认 web rendererhtml 的时候开发的。现在(Flutter 2, Dart 2.12),默认的渲染器是 canvaskit

    根据我的经验,这个包在 canvaskit 上表现有点奇怪,所以你应该使用 html 渲染器。

    为此,你必须运行普通的 flutter run -d chrome 命令,并带有 --web-renderer html 附加参数,如下所示

    flutter run -d chrome --web-renderer html

    用于运行,而

    flutter build web --web-renderer html

    用于构建。

  • Web和移动端行为差异

    请参阅 issues/#27

  • 关于Web内容加载

    为了使Web版本(iframe)正常工作,我不得不使用 x-frame bypass 的部分代码,以请求一个CORS代理,该代理会移除阻止iframe嵌入的请求头。

    这可能看起来像个 hack,而且确实是,但我找不到其他方法让iframe表现得与移动端webview(它是一种实际的浏览器,所以所有东西都可以默认在那里工作)相似。

  • 关于Web导航

    在Web端,历史导航栈是从头开始构建的,因为我无法正确处理iframe的内部历史。


已知问题和待办事项

  • [ x ] 在Web端,用户代理和请求头仅在使用 SourceType.urlBypass 时生效,并且仅在首次使用时有效(view/web.dart

  • [ x ] 在Web端,应该能够将加载 urlBypass 时捕获的任何错误发送到Dart回调,然后像移动端一样通过 onWebResourceError 回调发送(utils/x_frame_options_bypass.dart

  • [ x ] 在Web端,应该能够添加自定义代理列表,而无需JS空值检查的麻烦(utils/x_frame_options_bypass.dart

  • [ ? ] 最终(如果可能),WebSpecificParamsMobileSpecificParams 的大部分(如果不是全部)属性应该合并,而这两个对象可能会消失

  • [ x ] 在移动端,当在历史记录中前后移动时,控制器的值源类型会不同步。这是因为URL更改尚未被拦截并相应地设置模型(应该不难修复)。

  • 在移动端,如果操作失败,控制器的 callJsMethod 不会抛出错误。它只会在控制台中显示错误。

  • 添加测试

  • 列表已打开,可能还有其他

鸣谢

如果没有以下支持,这个包将无法实现

  • 最后但同样重要的是,http://deversoft.ro(我工作的公司)在我开发过程中给予了我动力

许可证

MIT

GitHub

查看 Github