【问题标题】:How to know if the user click a link inside a WebView in flutter?如何知道用户是否在 Flutter 中单击了 WebView 内的链接?
【发布时间】:2019-12-25 05:22:24
【问题描述】:

我正在在线阅读资源,但我不知道如何知道用户是否在 Flutter 中单击了 WebView 内的链接?我正在使用颤振团队的 WebView 插件。如有任何帮助,我将不胜感激。

【问题讨论】:

  • 您找到解决方案了吗?下面的所有自动检测链接甚至没有在 iOS 中点击

标签: flutter webview


【解决方案1】:

在 Webview 构造函数中使用 navigationDelegate 参数

WebView(
  initialUrl: 'https://google.com',
  navigationDelegate: (action) {
    if (action.url.contains('mail.google.com')) {
      print('Trying to open Gmail');
      Navigator.pop(context); // Close current window
      return NavigationDecision.prevent; // Prevent opening url
    } else if (action.url.contains('youtube.com')) {
      print('Trying to open Youtube');
      return NavigationDecision.navigate; // Allow opening url
    } else {
      return NavigationDecision.navigate; // Default decision
    }
  },
),

就是这样!

【讨论】:

    【解决方案2】:

    使用“request.url.startsWith('http')”对链接的所有点击进行处理:

    WebView(
          initialUrl: 'https://flutter.dev',
          javascriptMode: JavascriptMode.unrestricted,
          onWebViewCreated: (WebViewController webViewController) {
            _controller.complete(webViewController);
          },
          // TODO(iskakaushik): Remove this when collection literals makes it to stable.
          // ignore: prefer_collection_literals
          javascriptChannels: <JavascriptChannel>[
            _toasterJavascriptChannel(context),
          ].toSet(),
          navigationDelegate: (NavigationRequest request) {
            if (request.url.startsWith('http')) {
              //do somthing
              return NavigationDecision.prevent;
            }
            print('allowing navigation to $request');
            return NavigationDecision.navigate;
          },
            
    

    为我工作。

    【讨论】:

      【解决方案3】:

      我已经测试了示例代码。您可以使用 navigationDelegate 来做到这一点,
      例如,它会在点击时阻止导航 youtube。

      WebView(
                initialUrl: 'https://flutter.dev',
                javascriptMode: JavascriptMode.unrestricted,
                onWebViewCreated: (WebViewController webViewController) {
                  _controller.complete(webViewController);
                },
                // TODO(iskakaushik): Remove this when collection literals makes it to stable.
                // ignore: prefer_collection_literals
                javascriptChannels: <JavascriptChannel>[
                  _toasterJavascriptChannel(context),
                ].toSet(),
                navigationDelegate: (NavigationRequest request) {
                  if (request.url.startsWith('https://www.youtube.com/')) {
                    print('blocking navigation to $request}');
                    return NavigationDecision.prevent;
                  }
                  print('allowing navigation to $request');
                  return NavigationDecision.navigate;
                },
      

      完整示例代码

      // Copyright 2018 The Chromium Authors. All rights reserved.
      // Use of this source code is governed by a BSD-style license that can be
      // found in the LICENSE file.
      
      import 'dart:async';
      import 'dart:convert';
      import 'package:flutter/material.dart';
      import 'package:webview_flutter/webview_flutter.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 {
        @override
        _WebViewExampleState createState() => _WebViewExampleState();
      }
      
      class _WebViewExampleState extends State<WebViewExample> {
        final Completer<WebViewController> _controller =
        Completer<WebViewController>();
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            appBar: AppBar(
              title: const Text('Flutter WebView example'),
              // This drop down menu demonstrates that Flutter widgets can be shown over the web view.
              actions: <Widget>[
                NavigationControls(_controller.future),
                SampleMenu(_controller.future),
              ],
            ),
            // We're using a Builder here so we have a context that is below the Scaffold
            // to allow calling Scaffold.of(context) so we can show a snackbar.
            body: Builder(builder: (BuildContext context) {
              return WebView(
                initialUrl: 'https://flutter.dev',
                javascriptMode: JavascriptMode.unrestricted,
                onWebViewCreated: (WebViewController webViewController) {
                  _controller.complete(webViewController);
                },
                // TODO(iskakaushik): Remove this when collection literals makes it to stable.
                // ignore: prefer_collection_literals
                javascriptChannels: <JavascriptChannel>[
                  _toasterJavascriptChannel(context),
                ].toSet(),
                navigationDelegate: (NavigationRequest request) {
                  if (request.url.startsWith('https://www.youtube.com/')) {
                    print('blocking navigation to $request}');
                    return NavigationDecision.prevent;
                  }
                  print('allowing navigation to $request');
                  return NavigationDecision.navigate;
                },
                onPageFinished: (String url) {
                  print('Page finished loading: $url');
                },
              );
            }),
            floatingActionButton: favoriteButton(),
          );
        }
      
        JavascriptChannel _toasterJavascriptChannel(BuildContext context) {
          return JavascriptChannel(
              name: 'Toaster',
              onMessageReceived: (JavascriptMessage message) {
                Scaffold.of(context).showSnackBar(
                  SnackBar(content: Text(message.message)),
                );
              });
        }
      
        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();
                      Scaffold.of(context).showSnackBar(
                        SnackBar(content: Text('Favorited $url')),
                      );
                    },
                    child: const Icon(Icons.favorite),
                  );
                }
                return Container();
              });
        }
      }
      
      enum MenuOptions {
        showUserAgent,
        listCookies,
        clearCookies,
        addToCache,
        listCache,
        clearCache,
        navigationDelegate,
      }
      
      class SampleMenu extends StatelessWidget {
        SampleMenu(this.controller);
      
        final Future<WebViewController> controller;
        final CookieManager cookieManager = CookieManager();
      
        @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(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;
                  }
                },
                itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
                  PopupMenuItem<MenuOptions>(
                    value: MenuOptions.showUserAgent,
                    child: const Text('Show user agent'),
                    enabled: controller.hasData,
                  ),
                  const PopupMenuItem<MenuOptions>(
                    value: MenuOptions.listCookies,
                    child: Text('List cookies'),
                  ),
                  const PopupMenuItem<MenuOptions>(
                    value: MenuOptions.clearCookies,
                    child: Text('Clear cookies'),
                  ),
                  const PopupMenuItem<MenuOptions>(
                    value: MenuOptions.addToCache,
                    child: Text('Add to cache'),
                  ),
                  const PopupMenuItem<MenuOptions>(
                    value: MenuOptions.listCache,
                    child: Text('List cache'),
                  ),
                  const PopupMenuItem<MenuOptions>(
                    value: MenuOptions.clearCache,
                    child: Text('Clear cache'),
                  ),
                  const PopupMenuItem<MenuOptions>(
                    value: MenuOptions.navigationDelegate,
                    child: Text('Navigation Delegate example'),
                  ),
                ],
              );
            },
          );
        }
      
        void _onShowUserAgent(
            WebViewController controller, BuildContext context) async {
          // Send a message with the user agent string to the Toaster JavaScript channel we registered
          // with the WebView.
          controller.evaluateJavascript(
              'Toaster.postMessage("User Agent: " + navigator.userAgent);');
        }
      
        void _onListCookies(
            WebViewController controller, BuildContext context) async {
          final String cookies =
          await controller.evaluateJavascript('document.cookie');
          Scaffold.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.evaluateJavascript(
              'caches.open("test_caches_entry"); localStorage["test_localStorage"] = "dummy_entry";');
          Scaffold.of(context).showSnackBar(const SnackBar(
            content: Text('Added a test entry to cache.'),
          ));
        }
      
        void _onListCache(WebViewController controller, BuildContext context) async {
          await controller.evaluateJavascript('caches.keys()'
              '.then((cacheKeys) => JSON.stringify({"cacheKeys" : cacheKeys, "localStorage" : localStorage}))'
              '.then((caches) => Toaster.postMessage(caches))');
        }
      
        void _onClearCache(WebViewController controller, BuildContext context) async {
          await controller.clearCache();
          Scaffold.of(context).showSnackBar(const SnackBar(
            content: Text("Cache cleared."),
          ));
        }
      
        void _onClearCookies(BuildContext context) async {
          final bool hadCookies = await cookieManager.clearCookies();
          String message = 'There were cookies. Now, they are gone!';
          if (!hadCookies) {
            message = 'There are no cookies.';
          }
          Scaffold.of(context).showSnackBar(SnackBar(
            content: Text(message),
          ));
        }
      
        void _onNavigationDelegateExample(
            WebViewController controller, BuildContext context) async {
          final String contentBase64 =
          base64Encode(const Utf8Encoder().convert(kNavigationExamplePage));
          controller.loadUrl('data:text/html;base64,$contentBase64');
        }
      
        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
        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()) {
                        controller.goBack();
                      } else {
                        Scaffold.of(context).showSnackBar(
                          const SnackBar(content: Text("No back history item")),
                        );
                        return;
                      }
                    },
                  ),
                  IconButton(
                    icon: const Icon(Icons.arrow_forward_ios),
                    onPressed: !webViewReady
                        ? null
                        : () async {
                      if (await controller.canGoForward()) {
                        controller.goForward();
                      } else {
                        Scaffold.of(context).showSnackBar(
                          const SnackBar(
                              content: Text("No forward history item")),
                        );
                        return;
                      }
                    },
                  ),
                  IconButton(
                    icon: const Icon(Icons.replay),
                    onPressed: !webViewReady
                        ? null
                        : () {
                      controller.reload();
                    },
                  ),
                ],
              );
            },
          );
        }
      }
      

      演示信息

      【讨论】:

      • 我认为这并不能完全回答如何检测用户点击链接的问题。此示例不区分点击链接和页面重定向。
      猜你喜欢
      • 1970-01-01
      • 2018-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多