Flutter WebView插件webview_flutter_wkwebview_pagecall的使用

Flutter WebView插件webview_flutter_wkwebview 的使用

使用说明

webview_flutter_wkwebviewwebview_flutter 插件的 Apple WKWebView 实现。通过使用此插件,开发者可以在 Flutter 应用中轻松嵌入 WebView,并实现丰富的网页交互功能。

该插件已被官方推荐为 endorsed federated plugin,因此你可以直接使用 webview_flutter,而无需额外配置即可自动包含此插件的功能。


完整示例代码

以下是一个完整的 Flutter 示例代码,展示了如何使用 webview_flutter_wkwebview 插件来加载网页并实现一些高级功能,如导航拦截、JavaScript 通道等。

示例代码:example/lib/main.dart

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// ignore_for_file: public_member_api_docs

import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart';

import 'navigation_decision.dart';
import 'navigation_request.dart';
import 'web_view.dart';

void main() {
  runApp(MaterialApp(home: _WebViewExample()));
}

const String kNavigationExamplePage = '''
<!DOCTYPE html><html>
<head><title>Navigation Delegate Example</title></head>
<body>
<p>
The navigation delegate is set to block navigation to the youtube website.
</p>
<ul>
<ul><a href="https://www.youtube.com/">https://www.youtube.com/</a></ul>
<ul><a href="https://www.google.com/">https://www.google.com/</a></ul>
</ul>
</body>
</html>
''';

class _WebViewExample extends StatefulWidget {
  const _WebViewExample({Key? key}) : super(key: key);

  [@override](/user/override)
  _WebViewExampleState createState() => _WebViewExampleState();
}

class _WebViewExampleState extends State<_WebViewExample> {
  final Completer<WebViewController> _controller = Completer<WebViewController>();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter WebView 示例'),
        // 显示一个下拉菜单,展示 Flutter 小部件可以覆盖 WebView
        actions: <Widget>[
          _NavigationControls(_controller.future),
          _SampleMenu(_controller.future),
        ],
      ),
      // 使用 Builder 来获取一个 Scaffold 下的上下文,以便调用 Scaffold.of(context) 显示 SnackBar
      body: Builder(builder: (context) {
        return WebView(
          initialUrl: 'https://app.pagecall.com/meet?room_id=64c36ca02e98292a17562053',
          onWebViewCreated: (WebViewController controller) {
            print('onWebViewCreated!!');
            _controller.complete(controller);
          },
          javascriptChannels: {
            // 创建一个 JavaScript 通道,用于与 WebView 中的 JavaScript 进行通信
            JavascriptChannel(
                name: 'jurung',
                onMessageReceived: (JavascriptMessage message) {
                  print('JavascriptChannel: $message');
                }),
          },
          javascriptMode: JavascriptMode.unrestricted,
          navigationDelegate: (NavigationRequest request) {
            // 拦截导航请求,阻止访问 YouTube 网站
            if (request.url.startsWith('https://www.youtube.com/')) {
              print('阻止导航到 $request}');
              return NavigationDecision.prevent;
            }
            print('允许导航到 $request');
            return NavigationDecision.navigate;
          },
        );
      }),
      floatingActionButton: favoriteButton(),
    );
  }

  Widget favoriteButton() {
    return FutureBuilder<WebViewController>(
        future: _controller.future,
        builder: (BuildContext context, AsyncSnapshot<WebViewController> controller) {
          if (controller.hasData) {
            return FloatingActionButton(
              onPressed: () async {
                final String url = (await controller.data!.currentUrl())!;
                // 忽略:deprecated_member_use
              },
              child: const Icon(Icons.favorite),
            );
          }
          return Container();
        });
  }
}

Set<JavascriptChannel> _createJavascriptChannels(BuildContext context) {
  return {
    // 创建一个 JavaScript 通道,用于向用户显示 SnackBar 消息
    JavascriptChannel(
        name: 'Snackbar',
        onMessageReceived: (JavascriptMessage message) {
          ScaffoldMessenger.of(context)
              .showSnackBar(SnackBar(content: Text(message.message)));
        }),
  };
}

enum _MenuOptions {
  showUserAgent,
  listCookies,
  clearCookies,
  addToCache,
  listCache,
  clearCache,
  navigationDelegate,
  doPostRequest,
}

class _SampleMenu extends StatelessWidget {
  _SampleMenu(this.controller);

  final Future<WebViewController> controller;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: controller,
      builder:
          (BuildContext context, AsyncSnapshot<WebViewController> controller) {
        return PopupMenuButton<_MenuOptions>(
          onSelected: (_MenuOptions value) {
            switch (value) {
              case _MenuOptions.showUserAgent:
                _onShowUserAgent(controller.data!, context);
                break;
              case _MenuOptions.listCookies:
                _onListCookies(controller.data!, context);
                break;
              case _MenuOptions.clearCookies:
                _onClearCookies(controller.data!, context);
                break;
              case _MenuOptions.addToCache:
                _onAddToCache(controller.data!, context);
                break;
              case _MenuOptions.listCache:
                _onListCache(controller.data!, context);
                break;
              case _MenuOptions.clearCache:
                _onClearCache(controller.data!, context);
                break;
              case _MenuOptions.navigationDelegate:
                _onNavigationDelegateExample(controller.data!, context);
                break;
              case _MenuOptions.doPostRequest:
                _onDoPostRequest(controller.data!, context);
                break;
            }
          },
          itemBuilder: (BuildContext context) => <
              PopupMenuItem<_MenuOptions>>[
            PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.showUserAgent,
              child: const Text('显示用户代理'),
              enabled: controller.hasData,
            ),
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.listCookies,
              child: Text('列出 Cookie'),
            ),
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.clearCookies,
              child: Text('清除 Cookie'),
            ),
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.addToCache,
              child: Text('添加到缓存'),
            ),
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.listCache,
              child: Text('列出缓存'),
            ),
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.clearCache,
              child: Text('清除缓存'),
            ),
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.navigationDelegate,
              child: Text('导航委托示例'),
            ),
            const PopupMenuItem<_MenuOptions>(
              value: _MenuOptions.doPostRequest,
              child: Text('POST 请求'),
            ),
          ],
        );
      },
    );
  }

  void _onShowUserAgent(
      WebViewController controller, BuildContext context) async {
    // 发送一条消息到 WebView 中的 JavaScript,显示用户代理字符串
    await controller.runJavascript(
        'Snackbar.postMessage("User Agent: " + navigator.userAgent);');
  }

  void _onListCookies(
      WebViewController controller, BuildContext context) async {
    final String cookies =
        await controller.runJavascriptReturningResult('document.cookie');
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      content: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          const Text('Cookies:'),
          _getCookieList(cookies),
        ],
      ),
    ));
  }

  void _onAddToCache(WebViewController controller, BuildContext context) async {
    await controller.runJavascript(
        'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
      content: Text('添加了一个测试条目到缓存中。'),
    ));
  }

  void _onListCache(WebViewController controller, BuildContext context) async {
    await controller.runJavascript('caches.keys()'
        '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
        '.then((caches) => Snackbar.postMessage(caches))');
  }

  void _onClearCache(WebViewController controller, BuildContext context) async {
    await controller.clearCache();
    ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
      content: Text("缓存已清除。"),
    ));
  }

  void _onClearCookies(
      WebViewController controller, BuildContext context) async {
    final bool hadCookies = await WebView.platform.clearCookies();
    String message = '存在 Cookie。现在它们已经消失了!';
    if (!hadCookies) {
      message = '不存在 Cookie。';
    }
    ScaffoldMessenger.of(context).showSnackBar(SnackBar(
      content: Text(message),
    ));
  }

  void _onNavigationDelegateExample(
      WebViewController controller, BuildContext context) async {
    final String contentBase64 =
        base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
    await controller.loadUrl('data:text/html;base64,$contentBase64');
  }

  void _onDoPostRequest(
      WebViewController controller, BuildContext context) async {
    WebViewRequest request = WebViewRequest(
      uri: Uri.parse('https://httpbin.org/post'),
      method: WebViewRequestMethod.post,
      headers: {'foo': 'bar', 'Content-Type': 'text/plain'},
      body: Uint8List.fromList('Test Body'.codeUnits),
    );
    await controller.loadRequest(request);
  }

  Widget _getCookieList(String cookies) {
    if (cookies == null || cookies == '""') {
      return Container();
    }
    final List<String> cookieList = cookies.split(';');
    final Iterable<Text> cookieWidgets =
        cookieList.map((String cookie) => Text(cookie));
    return Column(
      mainAxisAlignment: MainAxisAlignment.end,
      mainAxisSize: MainAxisSize.min,
      children: cookieWidgets.toList(),
    );
  }
}

class _NavigationControls extends StatelessWidget {
  const _NavigationControls(this._webViewControllerFuture)
      : assert(_webViewControllerFuture != null);

  final Future<WebViewController> _webViewControllerFuture;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return FutureBuilder<WebViewController>(
      future: _webViewControllerFuture,
      builder:
          (BuildContext context, AsyncSnapshot<WebViewController> snapshot) {
        final bool webViewReady =
            snapshot.connectionState == ConnectionState.done;
        final WebViewController? controller = snapshot.data;

        return Row(
          children: <Widget>[
            IconButton(
              icon: const Icon(Icons.arrow_back_ios),
              onPressed: !webViewReady
                  ? null
                  : () async {
                      if (await controller!.canGoBack()) {
                        await controller.goBack();
                      } else {
                        // 忽略:deprecated_member_use
                        return;
                      }
                    },
            ),
            IconButton(
              icon: const Icon(Icons.arrow_forward_ios),
              onPressed: !webViewReady
                  ? null
                  : () async {
                      if (await controller!.canGoForward()) {
                        await controller.goForward();
                      } else {
                        // 忽略:deprecated_member_use
                        return;
                      }
                    },
            ),
            IconButton(
              icon: const Icon(Icons.replay),
              onPressed: !webViewReady
                  ? null
                  : () {
                      controller!.reload();
                    },
            ),
          ],
        );
      },
    );
  }
}

/// 回调类型,用于处理从 WebView 中 JavaScript 发送的消息。
typedef void JavascriptMessageHandler(JavascriptMessage message);

更多关于Flutter WebView插件webview_flutter_wkwebview_pagecall的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter WebView插件webview_flutter_wkwebview_pagecall的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


webview_flutter_wkwebview_pagecall 是一个 Flutter 插件,用于在 iOS 平台上使用 WKWebView 来加载网页内容,并且支持通过 JavaScript 与 Flutter 进行双向通信。这个插件是基于 webview_flutter 插件的一个扩展,专门为 iOS 平台提供了更强大的功能。

以下是如何使用 webview_flutter_wkwebview_pagecall 插件的基本步骤:

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 webview_flutter_wkwebview_pagecall 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  webview_flutter_wkwebview_pagecall: ^1.0.0 # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入插件

在你的 Dart 文件中导入插件:

import 'package:webview_flutter_wkwebview_pagecall/webview_flutter_wkwebview_pagecall.dart';

3. 创建 WebView

使用 WebView 控件来加载网页内容。你可以通过 WebViewController 来控制 WebView 的行为。

class MyWebView extends StatefulWidget {
  [@override](/user/override)
  _MyWebViewState createState() => _MyWebViewState();
}

class _MyWebViewState extends State<MyWebView> {
  late WebViewController _controller;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WebView Example'),
      ),
      body: WebView(
        initialUrl: 'https://flutter.dev',
        onWebViewCreated: (WebViewController webViewController) {
          _controller = webViewController;
        },
        javascriptMode: JavascriptMode.unrestricted,
        onPageStarted: (String url) {
          print('Page started loading: $url');
        },
        onPageFinished: (String url) {
          print('Page finished loading: $url');
        },
        gestureNavigationEnabled: true,
      ),
    );
  }
}

4. JavaScript 与 Flutter 通信

webview_flutter_wkwebview_pagecall 插件允许你通过 JavaScript 与 Flutter 进行双向通信。你可以通过 WebViewController 来注册 JavaScript 通道,并在 Flutter 中处理来自 JavaScript 的消息。

[@override](/user/override)
void initState() {
  super.initState();
  _controller = WebViewController()
    ..setJavaScriptMode(JavaScriptMode.unrestricted)
    ..addJavaScriptChannel('Flutter', onMessageReceived: (JavaScriptMessage message) {
      // 处理来自 JavaScript 的消息
      print('Received message from JavaScript: ${message.message}');
    })
    ..loadRequest(Uri.parse('https://flutter.dev'));
}

在 JavaScript 中,你可以通过 Flutter.postMessage() 来发送消息到 Flutter:

Flutter.postMessage('Hello from JavaScript!');

5. 处理页面加载事件

你可以通过 onPageStartedonPageFinished 回调来处理页面加载的开始和结束事件。

onPageStarted: (String url) {
  print('Page started loading: $url');
},
onPageFinished: (String url) {
  print('Page finished loading: $url');
},

6. 处理手势导航

你可以通过 gestureNavigationEnabled 来启用或禁用手势导航。

gestureNavigationEnabled: true,

7. 其他功能

webview_flutter_wkwebview_pagecall 还提供了其他一些功能,例如:

  • 自定义 UserAgent: 你可以通过 WebViewController 来设置自定义的 UserAgent。
  • Cookie 管理: 你可以通过 WebViewController 来管理 WebView 中的 Cookie。
  • JavaScript 执行: 你可以通过 WebViewController 在 WebView 中执行 JavaScript 代码。

8. 注意事项

  • iOS 平台: 这个插件是专门为 iOS 平台设计的,因此在 Android 平台上可能无法使用所有功能。
  • 权限: 确保在你的 Info.plist 文件中添加了必要的权限,例如网络访问权限。
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

9. 示例代码

以下是一个完整的示例代码:

import 'package:flutter/material.dart';
import 'package:webview_flutter_wkwebview_pagecall/webview_flutter_wkwebview_pagecall.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyWebView(),
    );
  }
}

class MyWebView extends StatefulWidget {
  [@override](/user/override)
  _MyWebViewState createState() => _MyWebViewState();
}

class _MyWebViewState extends State<MyWebView> {
  late WebViewController _controller;

  [@override](/user/override)
  void initState() {
    super.initState();
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..addJavaScriptChannel('Flutter', onMessageReceived: (JavaScriptMessage message) {
        print('Received message from JavaScript: ${message.message}');
      })
      ..loadRequest(Uri.parse('https://flutter.dev'));
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WebView Example'),
      ),
      body: WebView(
        initialUrl: 'https://flutter.dev',
        onWebViewCreated: (WebViewController webViewController) {
          _controller = webViewController;
        },
        javascriptMode: JavascriptMode.unrestricted,
        onPageStarted: (String url) {
          print('Page started loading: $url');
        },
        onPageFinished: (String url) {
          print('Page finished loading: $url');
        },
        gestureNavigationEnabled: true,
      ),
    );
  }
}
回到顶部