flutter 专题 七十 Flutter应用开发之webview_flutter插件

简介

在移动应用开发中,经常会遇到加载网页的需求,打开网页通常有两种方式,即在应用内使用内置的组件打开和使用系统自带的浏览器打开。不过,在Flutter应用开发中,由于官方并没有提供类似Webview的网页加载组件,所以如果项目中涉及网页加载需要使用第三方插件库,如webview_flutter、flutter_webview_plugin等。

其中,webview_flutter是Flutter官方开发和维护的网页加载插件库,而flutter_webview_plugin则是Flutter开源社区推出的网页加载插件。两个插件功能都差不多,都支持加载本地html文本、Flutter调用js以及js调用Flutter等,但是我建议使用官方推出的插件,因为它会持续的跟进已知的问题。

和其他Flutter插件的使用方式一样,使用webview_flutter之前需要先在pubspec.yaml文件中添加依赖脚本,如下所示。

复制代码
dependencies:
   webview_flutter: ^0.3.22+1

然后,我们使用flutter packages get命令将webview_flutter插件拉取到本地后,就可以使用它进行网页加载开发了。

基本使用

如下所示,是WebView组件的构造函数。

复制代码
WebView({
    Key key,
    this.onWebViewCreated,             //WebView创建完成之后的回调
    this.initialUrl,                               // 初始化 URL
    this.javascriptMode = JavascriptMode.disabled,    //JS执行模式,默认是不调用
    this.javascriptChannels,             // JS可以调用Flutter 的通道
    this.navigationDelegate,            // 路由委托,可以使用它执行拦截操作
    this.gestureRecognizers,          // 手势相关
    this.onPageStarted,                 //开始加载页面回调
    this.onPageFinished,              // 页面加载完成的回调
    this.onWebResourceError,     //资源加载失败回调
    this.debuggingEnabled = false,
    this.gestureNavigationEnabled = false,
    this.userAgent,
    this.initialMediaPlaybackPolicy =
        AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
  })

使用时,只需要按照参数传递对应的值即可。不过,在实际使用前,为了方便使用,我们一般会对它进行二次封装,主要是界面和功能的封装。下面是我封装的一个可以加载本地和网络文件的WebViewPage。

复制代码
class WebViewPage extends StatefulWidget {

  String url;
  final String title;
  final bool isLocalUrl;

  WebViewController _webViewController;

  WebViewPage({this.url, this.isLocalUrl = false, this.title});

  @override
  _WebViewPage createState() => _WebViewPage();


}

class _WebViewPage extends State<WebViewPage> {

  JavascriptChannel jsBridge(BuildContext context) => JavascriptChannel(
      name: 'jsbridge', // 与h5 端的一致 不然收不到消息
      onMessageReceived: (JavascriptMessage message) async{
        debugPrint(message.message);
      });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: _buildAppbar(),
        body: _buildBody()
    );
  }

  _buildAppbar() {
    return AppBar(
        elevation: 0,
        backgroundColor: Color(0xccd0d7),
        title: Text(widget.title, style: TextStyle(color: Colors.black),),
        centerTitle: true,
        leading: IconButton(icon: Icon(Icons.arrow_back, color: Color(0xFF23ADE5),), onPressed: () {

        })
    );
  }

  _buildBody() {
    return Column(
      children: <Widget>[
        SizedBox(
          height: 1,
          width: double.infinity,
          child: const DecoratedBox(decoration: BoxDecoration(color: Color(0xFFEEEEEE))),
        ),
        Expanded(
          flex: 1,
          child: WebView(
            initialUrl: widget.isLocalUrl ? Uri.dataFromString(widget.url, mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
                .toString(): widget.url,
            javascriptMode: JavascriptMode.unrestricted,
            javascriptChannels: <JavascriptChannel>[
              jsBridge(context)
            ].toSet(),
            onWebViewCreated: (WebViewController controller){
              widget._webViewController = controller;
              if(widget.isLocalUrl){
                  _loadHtmlAssets(controller);
              }else{
                controller.loadUrl(widget.url);
              }
              controller.canGoBack().then((value) => debugPrint(value.toString()));
              controller.canGoForward().then((value) => debugPrint(value.toString()));
              controller.currentUrl().then((value) => debugPrint(value));
            },
            onPageFinished: (String value){
              widget._webViewController.evaluateJavascript('document.title')
                  .then((title) => debugPrint(title));
            },
          ),
        )
      ],
    );
  }

//加载本地文件
  _loadHtmlAssets(WebViewController controller) async {
    String htmlPath = await rootBundle.loadString(widget.url);
    controller.loadUrl(Uri.dataFromString(htmlPath,mimeType: 'text/html', encoding: Encoding.getByName('utf-8'))
        .toString());
  }

}

使用时,只需要按照传入对应的属性即可。需要说明的是,加载本地Html文件时,需要在pubspec.yaml文件中申明这个Html文件,如下所示。

复制代码
flutter:
 //  ...
  assets:
   - assets/real_data_help.html

然后,我们使用封装的组件即可加载本地的Html文件。例如:

复制代码
class MyApp extends StatelessWidget {

  String localUrl = 'assets/real_data_help.html';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home:WebViewPage(url:localUrl, isLocalUrl: true, title: '加载本地文件'),
    );
  }
}

运行代码,效果下图所示。

相关推荐
奋斗的小青年!!11 小时前
Flutter浮动按钮在OpenHarmony平台的实践经验
flutter·harmonyos·鸿蒙
程序员老刘15 小时前
一杯奶茶钱,PicGo + 阿里云 OSS 搭建永久稳定的个人图床
flutter·markdown
奋斗的小青年!!18 小时前
OpenHarmony Flutter 拖拽排序组件性能优化与跨平台适配指南
flutter·harmonyos·鸿蒙
小雨下雨的雨19 小时前
Flutter 框架跨平台鸿蒙开发 —— Stack 控件之三维层叠艺术
flutter·华为·harmonyos
行者9620 小时前
OpenHarmony平台Flutter手风琴菜单组件的跨平台适配实践
flutter·harmonyos·鸿蒙
小雨下雨的雨1 天前
Flutter 框架跨平台鸿蒙开发 —— Flex 控件之响应式弹性布局
flutter·ui·华为·harmonyos·鸿蒙系统
cn_mengbei1 天前
Flutter for OpenHarmony 实战:CheckboxListTile 复选框列表项详解
flutter
cn_mengbei1 天前
Flutter for OpenHarmony 实战:Switch 开关按钮详解
flutter
奋斗的小青年!!1 天前
OpenHarmony Flutter实战:打造高性能订单确认流程步骤条
flutter·harmonyos·鸿蒙
Coder_Boy_1 天前
Flutter基础介绍-跨平台移动应用开发框架
spring boot·flutter