Flutter 在Windows上使用内嵌WebView

一、问题背景

对于Flutter的App开发来说,在一些场景下,我们需要用到内嵌的WebView来加载一些H5网页资源等。

在移动平台Android和IOS中,官方已经有插件来实现相关功能,webview_flutter,支持移动平台的内嵌WebView功能,使用简单,也可以通过源码导入的方式实现自定义功能。

在Flutter 3.0后,开始全面支持桌面平台,MacOS,Windows等。

我们的摸鱼kik App,作为资讯订阅类应用,在PC上也有很强的用户需求,所有需要开发桌面平台版本。

Window版本的摸鱼kik,当然也需要有相应的WebView插件来实现资讯内容的浏览功能。

下面主要来介绍一下在Windows平台上的内嵌WebView如何使用。
本文章暂时只介绍一个基本的插件使用和打包方式,之后会再通过源码层面介绍插件的具体情况,以及如何使用微软的fixed version版本,将网页内嵌到自己的源码中。

二、了解WebView2 Runtime

要使用WebView,我们需要先了解一下Windows平台上的WebView环境。

Microsoft Edge WebView2 是Windows平台的WebView内核。

控件允许在本机应用中嵌入 web 技术(HTML、CSS 以及 JavaScript)。 WebView2 控件使用 Microsoft Edge 作为绘制引擎,以在本机应用中显示 web 内容。
使用 WebView2 可以在本机应用的不同部分嵌入 Web 代码,或在单个 WebView2 实例中生成所有本机应用。

三、插件接入

对于移动平台Android和IOS来说,WebView插件包括两个部分,即Android和IOS原生部分代码实现,以及Dart语言实现的Flutter调用层。

对于Windows平台,原理也是一样的,即Windows API与C++实现的原生部分,以及Dart语言实现的Flutter调用层。

想要深度自定义的开发者,可以参考WebView2Samples,这是微软在Github上提供的Webview2库的相关代码与演示Demo。之后再自己实现Flutter层的MethodChannel调用即可。

仅想要使用WebView功能,不需要深度自定义,可以使用开发好的Flutter插件,webview_windows

四、 Webview_windows介绍

使用webview_windows进行开发,需要

  • Visual Studio 2019+版本
  • Window10 SDK
  • nuget.exe

运行包含WebView2的软件,需要电脑上安装以下环境

  • WebView2 Runtime

运行时环境后面会说明,如何保障没有安装WebView2 Runtime的电脑运行软件时可以正常使用。

其中前两项是需要安装在开发设备上的环境,nuget.exe需要我们放到工程根目录下。nuget下载路径,下载最左侧的exe文件即可。

环境配置好后,在项目配置中增加

dependencies:
  webview_windows: ^0.2.0

之后执行flutter pub get即可。

基本使用示例:

late WebviewController _controller;

  @override
  void initState() {
    super.initState();
    _controller = WebviewController();
    initPlatformState();
  }

  @override
  Widget build(BuildContext context) {
    return Webview(_controller);
  }

  Future<void> initPlatformState() async {
    // Optionally initialize the webview environment using
    // a custom user data directory
    // and/or a custom browser executable directory
    // and/or custom chromium command line flags
    //await WebviewController.initializeEnvironment(
    //    additionalArguments: '--show-fps-counter');

    try {
      await _controller.initialize();
      await _controller.setBackgroundColor(Colors.white);
      await _controller.setPopupWindowPolicy(WebviewPopupWindowPolicy.deny);
      await _controller.loadUrl(url);

      if (!mounted) return;
      setState(() {});
    } catch(e) {

    }
  }

至此,WebView已经可以在Windows上正常使用了。

这里以我目前开发的阅读App摸鱼Kik举例,目前支持Android与IOS,PC端过段时间即将上线,也欢迎下载体验

加载一个微博页面的效果大致如下,效果和电脑中的浏览器体验基本一致:

Windows上网页展示

五、WebView2 Runtime环境预安装

由于我们使用了WebView2,它只能在安装了WebView2 Runtime的电脑上运行,所以我们在对Windows软件进行打包时候,需要配置好安装环境。让用户在安装过程中无感知地装入WebView2 Runtime。

这里还是以摸鱼Kik举例,介绍一下Flutter的Windows平台项目如何进行打包和预安装环境的配置。

Visual Studio配置

1.找到SLN文件

SLN:解决方案文件。解决方案是用于在 Visual Studio 中组织项目的结构。

1.png

2.安装插件 installer project

在扩展-管理扩展中安装Microsoft Visual Studio Installer Project

插件本身体积不大,但可能涉及网络环境问题(墙)

2.png

3.为工程创建setup project

3.png

在弹出菜单中搜索Setup Project

4.png

在Setup Project创建完成后,会自动打开其配置窗口

5.png

这里面包含3个项目

  • Application Folder
  • User's Desktop
  • User's Programs Menu

Application Folder配置

Application Folder中存放项目所需要的所有文件

其中包括

  • 资源文件
  • 动态链接库
  • 项目执行文件

Visual Studio在打包时候将这些文件一并放到安装包中,在安装时候解压出来

首先为Application Folder添加项目输出

6.png

先将自己项目作为主输出,添加到Application Folder中

7.png

在添加成功后,可以在右侧看到我们的项目输出。其中包括Visual Studio自动添加的一些环境依赖的动态链接库

如MSVCP140.dll等等,是启动所需要的环境。(这里建议直接去系统C盘/Windows/System32下,将不带D的DLL文件拖进来。比如VCRUNTIME140_1.dll,VCRUNTIME140.dll。图中带D后缀的为Debug版本)

8.png

然后我们添加资源文件

Flutter的资源文件位置为:\工程目录\build\windows\runner\Debug(Release)

将其整体导入到Application Folder中,并保持层级不变

导入成功后结构如图所示:

9.png

之后再为项目添加额外的依赖,如Flutter插件所需要的动态链接库,这些文件要根据插件的需求进行添加

比如Flutter的数据库需要依赖sqlite3.dll

如果项目中有插件依赖一些额外的动态链接库,就需要手动添加进来。

在添加完成所有需要的资源后,初步完成Application Folder的配置

预安装环境配置

由于项目可能依赖一些额外的启动环境,如.net framework,WebView2Runtime等,我们需要将这些安装文件导入到我们的项目中,并在安装软件时候,通过引导程序为用户安装这些环境。

引导程序会先检测系统是否有所需要的运行环境,如果没有,则先安装环境,再安装软件。

整个过程对于用户来说是没有明显感知的,只需要一直进行下一步即可。

打开我们所创建的 Setup Project的属性,在此处打开Prerequisites配置。

10.png

Prerequisites包含所支持的环境,这里面有3个选项

11.png
  • 从组件供应商的网站上下载系统必备组件
  • 从与我的应用程序相同的位置下载系统必备组件
  • 从下列位置下载系统必备组件

其中第一和第三个选项都是从网络上下载,区别在于是从官方网站还是自己所配置的服务器上下载。

第二个选项是我们来提供安装包,一同打包到安装包中,在安装开始时候,引导程序如果发现环境不满足运行,先来安装我们提供的这些文件,再去安装软件。

如何配置预安装包

Visual Studio会从以下位置查找环境安装包

C:\Program Files (x86)\Microsoft SDKs\ClickOnce Bootstrapper\Packages

其中每个环境对于的子文件夹路径不一样,如.net framework对应DoNetFX,后面的数字表示版本号,如472代表4.7.2

12.png

打开任意需要的环境路径,这里以DotNetFX472举例。初始状态如下

13.png

每个文件夹对应一种语言。

这个语言是引导程序在安装时候为用户展示的界面

其中zh-Hans是简体中文

我们需要为打包提供2个安装包,程序安装包和语言包

每个语言路径下有Eula和Package两个文件

Eula:微软软件补充程序许可条款

Package:是对引导程序的翻译,并指明了我们需要在哪里下载语言包

搜索fwlink即可找到:

http://go.microsoft.com/fwlink/?LinkId=863262&clcid=0x804

<?xml version="1.0" encoding="UTF-8"?>

-<Package LicenseAgreement="eula.rtf" Culture="Culture" Name="DisplayName" xmlns="http://schemas.microsoft.com/developer/2004/01/bootstrapper">

<!-- Defines list of files to be copied on build -->

-<PackageFiles CopyAllPackageFiles="false">

<PackageFile Name="eula.rtf"/>

<PackageFile Name="NDP472-KB4054530-x86-x64-AllOS-CHS.exe" PublicKey="3082010a0282010100b6bb19591000a3a9f1e4b85ca80b07cbdb9a1f23d0d958ab78c048f7241438f063edd54b03bfcdf809ca1450f327b3fe82b4fa1a4384e1cbf91b38e83fcc9027ac97a2310a917b62ec75bfcd488da05d75fd95a775ff23d40ce5e8e063703e35ead49662f87655f756d4afa663cde3e3d60f9b7a9b2a77f1c2d5749c8f47d3dda0312d1ca4252cc4c4406653d792cae3b052eabe0ab8ae750b5618c74953ae74bff2a6cdf338e98ea5a3f3029e7a6fcf7cc42fb3cc754770aa3fe7621186dd45482f15fbb4074c6f3ecb37ee96388a53d40e35af06a83959effbc51af781f0863666bb54bbdf2795ed1659371a2111e8098bd618b2c5daed465452408203e2a70203010001" HomeSite="DotNetFX472FullLanguagePackBootstrapper"/>

</PackageFiles>

-<InstallChecks>

<RegistryCheck Value="Release" Key="HKLM\Software\Microsoft\NET Framework Setup\NDP\v4\Full\2052" Property="DotNetFullLangPack_Release"/>

<RegistryCheck Value="LCID" Key="HKLM\SYSTEM\CurrentControlSet\Control\MUI\UILanguages\zh-CN" Property="DotNetFullLangPack_OS_LCID"/>

<RegistryCheck Value="v4" Key="HKLM\SOFTWARE\Microsoft\NET Framework Setup\OS Integration" Property="DotNetFullLangPack_OSIntegrated"/>

</InstallChecks>

<!-- Defines how to invoke the setup for .NET Framework redist -->

-<Commands>

-<Command EstimatedInstallSeconds="90" EstimatedTempBytes="77473415" EstimatedInstalledBytes="130718421" Arguments=" /q /norestart /skipenucheck /ChainingPackage FullX64ClickOnce" PackageFile="NDP472-KB4054530-x86-x64-AllOS-CHS.exe">

<!-- These checks determine whether the package is to be installed in the cases where .NET is not integrated into the OS (MSI install)-->

-<InstallConditions>

<!-- This indicates .NET Framework is already installed -->

<BypassIf Value="461808" Property="DotNetFullLangPack_Release" Compare="ValueGreaterThanOrEqualTo"/>

<!-- If netfx is part of the OS, then bypass this command. -->

<BypassIf Value="1" Property="DotNetFullLangPack_OSIntegrated" Compare="ValueEqualTo"/>

<!-- Block install if user does not have admin privileges -->

<FailIf Value="false" Property="AdminUser" Compare="ValueEqualTo" String="AdminRequired"/>

<!-- Block install on less than Windows 7 RTM -->

<FailIf Value="6.1.0" Property="VersionNT" Compare="VersionLessThan" String="InvalidPlatformWinNT"/>

<!-- Block install if the platform is IA-64 -->

<FailIf Value="IA64" Property="ProcessorArchitecture" Compare="ValueEqualTo" String="InvalidPlatformArchitecture"/>

</InstallConditions>

-<ExitCodes>

<ExitCode Value="0" Result="Success"/>

<ExitCode Value="3010" Result="SuccessReboot"/>

<DefaultExitCode String="GeneralFailure" Result="Success" FormatMessageFromSystem="false"/>

</ExitCodes>

</Command>

-<Command EstimatedInstallSeconds="90" EstimatedTempBytes="77473415" EstimatedInstalledBytes="130718421" Arguments=" /q /norestart /skipenucheck /ChainingPackage FullX64ClickOnce" PackageFile="NDP472-KB4054530-x86-x64-AllOS-CHS.exe">

<!-- These checks determine whether the package is to be installed in the cases where .NET is integrated into the OS -->

-<InstallConditions>

<!-- This indicates .NET Framework is already installed -->

<BypassIf Value="461808" Property="DotNetFullLangPack_Release" Compare="ValueGreaterThanOrEqualTo"/>

<!-- If netfx is not part of the OS, then bypass this command. -->

<BypassIf Value="1" Property="DotNetFullLangPack_OSIntegrated" Compare="ValueNotEqualTo"/>

<BypassIf Value="1" Property="DotNetFullLangPack_OSIntegrated" Compare="ValueNotExists"/>

<!-- Do not attempt to install on OS where NetFx is integrated in OS and MUI LCID doesn't match with this package. -->

<BypassIf Value="2052" Property="DotNetFullLangPack_OS_LCID" Compare="ValueNotEqualTo"/>

<!-- Block install if user does not have admin privileges -->

<FailIf Value="false" Property="AdminUser" Compare="ValueEqualTo" String="AdminRequired"/>

<!-- Block install on OS less than Windows 8.0 -->

<FailIf Value="6.2.0" Property="VersionNT" Compare="VersionLessThan" String="InvalidPlatformWinNT"/>

<!-- Block install if the platform is IA-64 -->

<FailIf Value="IA64" Property="ProcessorArchitecture" Compare="ValueEqualTo" String="InvalidPlatformArchitecture"/>

</InstallConditions>

-<ExitCodes>

<ExitCode Value="0" Result="Success"/>

<ExitCode Value="3010" Result="SuccessReboot"/>

<DefaultExitCode String="GeneralFailure" Result="Success" FormatMessageFromSystem="false"/>

</ExitCodes>

</Command>

</Commands>

<!-- Defines a localizable string table for error messages-->

-<Strings>

<String Name="DisplayName">Microsoft .NET Framework 4.7.2 (x86 和 x64)</String>

<String Name="Culture">zh-Hans</String>

<String Name="AdminRequired">安装 Microsoft .NET Framework 4.7.2 需要管理员权限。请与管理员联系。</String>

<String Name="InvalidPlatformWinNT">在此操作系统上不支持安装 Microsoft .NET Framework 4.7.2。请与应用程序供应商联系。</String>

<String Name="InvalidPlatformArchitecture">在 IA-64 操作系统上不支持该版本的 Microsoft .NET Framework 4.7.2。请与应用供应商联系。</String>

<String Name="DotNetFX472FullWebBootstrapper">http://go.microsoft.com/fwlink/?LinkId=863262&clcid=0x804</String>

<String Name="DotNetFX472FullLanguagePackBootstrapper">http://go.microsoft.com/fwlink/?LinkId=863258&clcid=0x804</String>

<String Name="GeneralFailure">尝试安装 Microsoft .NET Framework 4.7.2 时出现故障。</String>

</Strings>

</Package>

配置Edge WebView Runtime环境

对于Flutter Windows上的Webview,使用了微软的WebView2 Runtime环境

在我们打包时候,需要添加这个环境的依赖,并且和其他环境一样提供安装程序。

目前这个环境需要我们手动添加配置:

1.在C:\Program Files (x86)\Microsoft SDKs\ClickOnce Bootstrapper\Package*路径下创建WebViewRuntime*

路径如下:

C:\Program Files (x86)\Microsoft SDKs\ClickOnce Bootstrapper\Packages\WebViewRuntime

2.将Webview2的安装包放在此路径下面

安装包下载:WebView2 - Microsoft Edge Developer

  • Evergreen Bootstrapper,这是一个下载+安装的包,打开后会自动下载最小的runtime包安装
  • Evergreen Standalone Installer,这是完整的离线安装包
  • Fixed Version,这是一个固定版本的,可以内嵌到软件内部的压缩包。你可以在软件里嵌入Webview2,这样可以独立于系统中的Webview2存在,软件会使用你自己的Webview2 Runtime,但是会增加安装包的体积。

建议使用Evergreen Bootstrapper

3.配置Product、Package、Eula

仅有安装包并不能直接使用,需要使用配套的引导文件

Product文件:

<?xml version="1.0" encoding="utf-8" ?>
<!--
***********************************************************************

  Copyright (C) Microsoft Corporation.  All rights reserved.

 THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
 KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 PARTICULAR PURPOSE.
***********************************************************************
-->

<Product 
  xmlns="http://schemas.microsoft.com/developer/2004/01/bootstrapper" 
  ProductCode="Microsoft.EdgeRuntime">

  <!-- Defines the list of files to be copied on build. -->
  <PackageFiles CopyAllPackageFiles="false">
    <PackageFile Name="MicrosoftEdgeWebview2Setup.exe" HomeSite="WebViewRuntime" PublicKey="0"/>
  </PackageFiles>

  <InstallChecks>
    <RegistryCheck Property="EdgeRuntimeVersionInstalled" Key="HKLM\SOFTWARE\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}" Value="pv"/>
  </InstallChecks>

  <!-- Defines how to run the Setup package. -->
  <Commands Reboot="Defer">
    <Command 
      PackageFile="MicrosoftEdgeWebview2Setup.exe" 
      Arguments=" /silent /installelevated /install" 
      EstimatedInstalledBytes="2600000" 
      EstimatedTempBytes="4500000"
      EstimatedInstallSeconds="60">
      <InstallConditions>
        <BypassIf Property="EdgeRuntimeVersionInstalled" Compare="VersionGreaterThanOrEqualTo" Value="86.0.622.15"/>
      </InstallConditions>
      <ExitCodes>
        <ExitCode Value="0" Result="Success"/>
        <DefaultExitCode Result="Fail" FormatMessageFromSystem="true" String="GeneralFailure" />
      </ExitCodes>
    </Command>
  </Commands>
</Product>

Package文件:

<?xml version="1.0" encoding="utf-8" ?>
<!--
***********************************************************************

  Copyright (C) Microsoft Corporation.  All rights reserved.

 THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
 KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 PARTICULAR PURPOSE.
***********************************************************************
-->

<Package 
  xmlns="http://schemas.microsoft.com/developer/2004/01/bootstrapper"
  Name="DisplayName"
  Culture="Culture"
  LicenseAgreement="placeholdereula.txt">
  <PackageFiles>
    <PackageFile Name="placeholdereula.txt"/>
  </PackageFiles>

  <!-- Defines a localizable string table for messages. -->
  <Strings>
    <String Name="DisplayName">Edge WebView runtime bootstrapper</String>
    <String Name="Culture">en</String>
    <String Name="AdminRequired">Administrator permissions are required to install the Microsoft Edge WebView runtime. Contact your administrator.</String>
    <String Name="GeneralFailure">A failure occurred attempting to install the Microsoft Edge WebView runtime.</String>
    <String Name="WebViewRuntime">http://go.microsoft.com/fwlink/p/?LinkId=2124703</String>
  </Strings>
</Package>

Eula文件:

Eula文件是一个纯文本文件,里面包含协议的内容等。由Package文件引用。

配置完成后即可重新生成 Setup Project。成功后即可生成安装文件。


打包完成效果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容