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: '加载本地文件'),
    );
  }
}

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

相关推荐
Lanren的编程日记25 分钟前
Flutter鸿蒙应用开发:基础UI组件库设计与实现实战
flutter·ui·harmonyos
西西学代码34 分钟前
Flutter---波形动画
flutter
于慨4 小时前
flutter基础组件用法
开发语言·javascript·flutter
恋猫de小郭6 小时前
Android CLI ,谷歌为 Android 开发者专研的 AI Agent,提速三倍
android·前端·flutter
火柴就是我7 小时前
flutter pushAndRemoveUntil 的一次小疑惑
flutter
于慨7 小时前
flutter doctor问题解决
flutter
唔668 小时前
flutter 图片加载类 图片的安全使用
安全·flutter
Nathan202406169 小时前
Flutter - InheritedWidget
flutter·dart
恋猫de小郭9 小时前
JetBrains Amper 0.10 ,期待它未来替代 Gradle
android·前端·flutter
Lanren的编程日记10 小时前
Flutter鸿蒙应用开发:实时聊天功能集成实战
flutter·华为·harmonyos