【问题标题】:Cookie String in Flutter WebViewFlutter WebView 中的 Cookie 字符串
【发布时间】:2020-05-01 03:11:53
【问题描述】:

我尝试使用普通的 Flutter webview 从给定的 url 获取 cookie。但是,我找不到任何相同的标准方法。 如何从 Flutter WebView 中的给定 url 获取 cookie 字符串?

【问题讨论】:

    标签: flutter webview


    【解决方案1】:

    官方版WebView
    你需要使用controller.evaluateJavascript('document.cookie');
    代码sn-p

    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),
          ],
        ),
      ));
    }
    

    详细信息http://www.coderzheaven.com/2019/03/28/webview-in-flutter-part-2/
    完整示例代码

    import 'package:flutter/material.dart';
    import 'package:webview_flutter/webview_flutter.dart';
    import 'dart:async';
    import 'dart:convert';
    
    const String examplepage = '''
          <!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 WebviewDemo extends StatefulWidget {
      final String title;
    
      WebviewDemo({Key key, this.title}) : super(key: key);
    
      @override
      State<StatefulWidget> createState() {
        return _WebviewDemoState();
      }
    }
    
    JavascriptChannel snackbarJavascriptChannel(BuildContext context) {
      return JavascriptChannel(
        name: 'SnackbarJSChannel',
        onMessageReceived: (JavascriptMessage message) {
          Scaffold.of(context).showSnackBar(SnackBar(
            content: Text(message.message),
          ));
        },
      );
    }
    
    class _WebviewDemoState extends State<WebviewDemo> {
      //
      final Completer<WebViewController> _controller =
          Completer<WebViewController>();
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(
            title: new Text('Webview Demo'),
            actions: <Widget>[
              NavigationControls(_controller.future),
              SampleMenu(_controller.future)
            ],
          ),
          body: Builder(
            builder: (BuildContext context) {
              return WebView(
                initialUrl: 'http://flutter.dev',
                javascriptMode: JavascriptMode.unrestricted,
                onWebViewCreated: (WebViewController webViewController) {
                  _controller.complete(webViewController);
                },
                javascriptChannels: <JavascriptChannel>[
                  snackbarJavascriptChannel(context),
                ].toSet(),
                navigationDelegate: (NavigationRequest request) {
                  if (request.url.startsWith("https://www.youtube.com")) {
                    print("Blocking navigation");
                    return NavigationDecision.prevent;
                  }
                  return NavigationDecision.navigate;
                },
              );
            },
          ),
        );
      }
    }
    
    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>(
              itemBuilder: (BuildContext context) => <PopupMenuItem<MenuOptions>>[
                    PopupMenuItem(
                      value: MenuOptions.showUserAgent,
                      child: const Text('Show User Agent'),
                      enabled: controller.hasData,
                    ),
                    PopupMenuItem(
                      value: MenuOptions.listCookies,
                      child: const Text('List Cookies'),
                    ),
                    PopupMenuItem(
                      value: MenuOptions.clearCookies,
                      child: const Text('Clear Cookies'),
                    ),
                    PopupMenuItem(
                      value: MenuOptions.addToCache,
                      child: const Text('Add to Cache'),
                    ),
                    PopupMenuItem(
                      value: MenuOptions.listCache,
                      child: const Text('List Cache'),
                    ),
                    PopupMenuItem(
                      value: MenuOptions.clearCache,
                      child: const Text('Clear Cache'),
                    ),
                    PopupMenuItem(
                      value: MenuOptions.navigationDelegate,
                      child: const Text('Navigation Delegate Demo'),
                    )
                  ],
              onSelected: (MenuOptions value) {
                switch (value) {
                  case MenuOptions.showUserAgent:
                    showUserAgent(controller.data, context);
                    break;
                  case MenuOptions.listCookies:
                    listCookies(controller.data, context);
                    break;
                  case MenuOptions.clearCookies:
                    clearCookies(controller.data, context);
                    break;
                  case MenuOptions.addToCache:
                    addToCache(controller.data, context);
                    break;
                  case MenuOptions.listCache:
                    listCache(controller.data, context);
                    break;
                  case MenuOptions.clearCache:
                    clearCache(controller.data, context);
                    break;
                  case MenuOptions.navigationDelegate:
                    navigationDelegateDemo(controller.data, context);
                    break;
                  default:
                }
              },
            );
          },
        );
      }
    
      navigationDelegateDemo(
          WebViewController controller, BuildContext context) async {
        final String contentbase64 =
            base64Encode(const Utf8Encoder().convert(examplepage));
        controller.loadUrl('data:text/html;base64,$contentbase64');
      }
    
      addToCache(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 listCache(WebViewController controller, BuildContext context) async {
        await controller.evaluateJavascript(
            'caches.keys().then((cacheKeys) => JSON.stringify({"cacheKeys": cacheKeys, "localStorage": localStorage })).then((caches) => SnackbarJSChannel.postMessage(caches))');
      }
    
      void clearCache(WebViewController controller, BuildContext context) async {
        await controller.clearCache();
        Scaffold.of(context).showSnackBar(
          const SnackBar(
            content: Text('Cache Cleared'),
          ),
        );
      }
    
      listCookies(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:'), getCookies(cookies)],
            ),
          ),
        );
      }
    
      Widget getCookies(String cookies) {
        if (null == cookies || cookies.isEmpty) {
          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(),
        );
      }
    
      void clearCookies(WebViewController controller, BuildContext context) async {
        final bool hadCookies = await cookieManager.clearCookies();
        String message = 'There are no cookies';
        Scaffold.of(context).showSnackBar(
          SnackBar(
            content: Text(message),
          ),
        );
      }
    
      showUserAgent(WebViewController controller, BuildContext context) {
        controller.evaluateJavascript(
            'SnackbarJSChannel.postMessage("User Agent: " + navigator.userAgent);');
      }
    }
    
    class NavigationControls extends StatelessWidget {
      const NavigationControls(this._webViewControllerFuture);
      final Future<WebViewController> _webViewControllerFuture;
    
      @override
      Widget build(BuildContext context) {
        return FutureBuilder(
          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"),
                              ),
                            );
                          }
                        },
                ),
                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"),
                              ),
                            );
                          }
                        },
                ),
                IconButton(
                  icon: const Icon(Icons.refresh),
                  onPressed: !webViewReady
                      ? null
                      : () async {
                          controller.reload();
                        },
                )
              ],
            );
          },
        );
      }
    }
    

    社区版 WebView
    你可以使用https://pub.flutter-io.cn/packages/flutter_webview_plugin

    代码sn-p

    RaisedButton(
                  onPressed: () {
                    flutterWebViewPlugin.getCookies().then((m) {
                      setState(() {
                        _history.add('cookies: $m');
                      });
                    });
                  },
                  child: const Text('Cookies'),
                )
    

    工作演示

    完整代码

    import 'dart:async';
    
    import 'package:flutter/material.dart';
    
    import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
    
    const kAndroidUserAgent =
        'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Mobile Safari/537.36';
    
    String selectedUrl = 'https://flutter.io';
    
    // ignore: prefer_collection_literals
    final Set<JavascriptChannel> jsChannels = [
      JavascriptChannel(
          name: 'Print',
          onMessageReceived: (JavascriptMessage message) {
            print(message.message);
          }),
    ].toSet();
    
    void main() {
      WidgetsFlutterBinding.ensureInitialized();
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      final flutterWebViewPlugin = FlutterWebviewPlugin();
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter WebView Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          routes: {
            '/': (_) => const MyHomePage(title: 'Flutter WebView Demo'),
            '/widget': (_) {
              return WebviewScaffold(
                url: selectedUrl,
                javascriptChannels: jsChannels,
                mediaPlaybackRequiresUserGesture: false,
                appBar: AppBar(
                  title: const Text('Widget WebView'),
                ),
                withZoom: true,
                withLocalStorage: true,
                hidden: true,
                initialChild: Container(
                  color: Colors.redAccent,
                  child: const Center(
                    child: Text('Waiting.....'),
                  ),
                ),
                bottomNavigationBar: BottomAppBar(
                  child: Row(
                    children: <Widget>[
                      IconButton(
                        icon: const Icon(Icons.arrow_back_ios),
                        onPressed: () {
                          flutterWebViewPlugin.goBack();
                        },
                      ),
                      IconButton(
                        icon: const Icon(Icons.arrow_forward_ios),
                        onPressed: () {
                          flutterWebViewPlugin.goForward();
                        },
                      ),
                      IconButton(
                        icon: const Icon(Icons.autorenew),
                        onPressed: () {
                          flutterWebViewPlugin.reload();
                        },
                      ),
                    ],
                  ),
                ),
              );
            },
          },
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      // Instance of WebView plugin
      final flutterWebViewPlugin = FlutterWebviewPlugin();
    
      // On destroy stream
      StreamSubscription _onDestroy;
    
      // On urlChanged stream
      StreamSubscription<String> _onUrlChanged;
    
      // On urlChanged stream
      StreamSubscription<WebViewStateChanged> _onStateChanged;
    
      StreamSubscription<WebViewHttpError> _onHttpError;
    
      StreamSubscription<double> _onProgressChanged;
    
      StreamSubscription<double> _onScrollYChanged;
    
      StreamSubscription<double> _onScrollXChanged;
    
      final _urlCtrl = TextEditingController(text: selectedUrl);
    
      final _codeCtrl = TextEditingController(text: 'window.navigator.userAgent');
    
      final _scaffoldKey = GlobalKey<ScaffoldState>();
    
      final _history = [];
    
      @override
      void initState() {
        super.initState();
    
        flutterWebViewPlugin.close();
    
        _urlCtrl.addListener(() {
          selectedUrl = _urlCtrl.text;
        });
    
        // Add a listener to on destroy WebView, so you can make came actions.
        _onDestroy = flutterWebViewPlugin.onDestroy.listen((_) {
          if (mounted) {
            // Actions like show a info toast.
            _scaffoldKey.currentState.showSnackBar(
                const SnackBar(content: const Text('Webview Destroyed')));
          }
        });
    
        // Add a listener to on url changed
        _onUrlChanged = flutterWebViewPlugin.onUrlChanged.listen((String url) {
          if (mounted) {
            setState(() {
              _history.add('onUrlChanged: $url');
            });
          }
        });
    
        _onProgressChanged =
            flutterWebViewPlugin.onProgressChanged.listen((double progress) {
          if (mounted) {
            setState(() {
              _history.add('onProgressChanged: $progress');
            });
          }
        });
    
        _onScrollYChanged =
            flutterWebViewPlugin.onScrollYChanged.listen((double y) {
          if (mounted) {
            setState(() {
              _history.add('Scroll in Y Direction: $y');
            });
          }
        });
    
        _onScrollXChanged =
            flutterWebViewPlugin.onScrollXChanged.listen((double x) {
          if (mounted) {
            setState(() {
              _history.add('Scroll in X Direction: $x');
            });
          }
        });
    
        _onStateChanged =
            flutterWebViewPlugin.onStateChanged.listen((WebViewStateChanged state) {
          if (mounted) {
            setState(() {
              _history.add('onStateChanged: ${state.type} ${state.url}');
            });
          }
        });
    
        _onHttpError =
            flutterWebViewPlugin.onHttpError.listen((WebViewHttpError error) {
          if (mounted) {
            setState(() {
              _history.add('onHttpError: ${error.code} ${error.url}');
            });
          }
        });
      }
    
      @override
      void dispose() {
        // Every listener should be canceled, the same should be done with this stream.
        _onDestroy.cancel();
        _onUrlChanged.cancel();
        _onStateChanged.cancel();
        _onHttpError.cancel();
        _onProgressChanged.cancel();
        _onScrollXChanged.cancel();
        _onScrollYChanged.cancel();
    
        flutterWebViewPlugin.dispose();
    
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          key: _scaffoldKey,
          appBar: AppBar(
            title: const Text('Plugin example app'),
          ),
          body: SingleChildScrollView(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                Container(
                  padding: const EdgeInsets.all(24.0),
                  child: TextField(controller: _urlCtrl),
                ),
                RaisedButton(
                  onPressed: () {
                    flutterWebViewPlugin.launch(
                      selectedUrl,
                      rect: Rect.fromLTWH(
                          0.0, 0.0, MediaQuery.of(context).size.width, 300.0),
                      userAgent: kAndroidUserAgent,
                      invalidUrlRegex:
                          r'^(https).+(twitter)', // prevent redirecting to twitter when user click on its icon in flutter website
                    );
                  },
                  child: const Text('Open Webview (rect)'),
                ),
                RaisedButton(
                  onPressed: () {
                    flutterWebViewPlugin.launch(selectedUrl, hidden: true);
                  },
                  child: const Text('Open "hidden" Webview'),
                ),
                RaisedButton(
                  onPressed: () {
                    flutterWebViewPlugin.launch(selectedUrl);
                  },
                  child: const Text('Open Fullscreen Webview'),
                ),
                RaisedButton(
                  onPressed: () {
                    Navigator.of(context).pushNamed('/widget');
                  },
                  child: const Text('Open widget webview'),
                ),
                Container(
                  padding: const EdgeInsets.all(24.0),
                  child: TextField(controller: _codeCtrl),
                ),
                RaisedButton(
                  onPressed: () {
                    final future =
                        flutterWebViewPlugin.evalJavascript(_codeCtrl.text);
                    future.then((String result) {
                      setState(() {
                        _history.add('eval: $result');
                      });
                    });
                  },
                  child: const Text('Eval some javascript'),
                ),
                RaisedButton(
                  onPressed: () {
                    setState(() {
                      _history.clear();
                    });
                    flutterWebViewPlugin.close();
                  },
                  child: const Text('Close'),
                ),
                RaisedButton(
                  onPressed: () {
                    flutterWebViewPlugin.getCookies().then((m) {
                      setState(() {
                        _history.add('cookies: $m');
                      });
                    });
                  },
                  child: const Text('Cookies'),
                ),
                Text(_history.join('\n'))
              ],
            ),
          ),
        );
      }
    }
    

    【讨论】:

    • 谢谢@chunhunghan!。如果有任何问题,我会尝试上述方法并回复您。
    【解决方案2】:

    如果你想在flutter_webview中使用Cookie,现在你可以使用 https://pub.dev/packages/webview_cookie_manager

    【讨论】:

    • 此插件适用于更高版本的 Android sdks,但不适用于 Marshmallow。
    【解决方案3】:

    webview_flutter 插件不提供使用其CookieManager 获取cookie 的方法。 相反,你可以使用我的插件flutter_inappwebview,这是一个 Flutter 插件,允许你添加内联 WebViews 或打开应用内浏览器窗口,并且有很多事件、方法和选项来控制 WebViews。

    它还为 Android 和 iOS 平台实现了CookieManager! 要获取 cookie,只需使用 CookieManager.instance().getCookies(url: url);

    这是一个在 WebView 停止加载时打印该 URL 的所有 cookie 列表的示例:

    import 'dart:async';
    import 'package:flutter/material.dart';
    import 'package:flutter_inappwebview/flutter_inappwebview.dart';
    
    Future main() async {
      WidgetsFlutterBinding.ensureInitialized();
      runApp(MyApp());
    }
    
    class MyApp extends StatefulWidget {
      @override
      _MyAppState createState() => new _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      InAppWebViewController _webViewController;
      CookieManager _cookieManager = CookieManager.instance();
    
      @override
      void initState() {
        super.initState();
      }
    
      @override
      void dispose() {
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: const Text('InAppWebView Example'),
            ),
            body: Container(
                child: Column(children: <Widget>[
                  Expanded(
                      child: InAppWebView(
                        initialUrl: "https://github.com/flutter",
                        initialHeaders: {},
                        initialOptions: InAppWebViewGroupOptions(
                          crossPlatform: InAppWebViewOptions(
                            debuggingEnabled: true,
                          ),
                        ),
                        onWebViewCreated: (InAppWebViewController controller) {
                          _webViewController = controller;
                        },
                        onLoadStart: (InAppWebViewController controller, String url) {
    
                        },
                        onLoadStop: (InAppWebViewController controller, String url) async {
                          List<Cookie> cookies = await _cookieManager.getCookies(url: url);
                          cookies.forEach((cookie) {
                            print(cookie.name + " " + cookie.value);
                          });
                        },
                      ))
                ])),
          ),
        );
      }
    }
    

    【讨论】:

    • 嘿,这个包是否保存了加载的url中的所有cookie?
    • 这个包适用于奥利奥和更高版本。不知何故,它不适用于棉花糖:/。有什么想法吗?
    • 如果您有任何问题,请在 GitHub 存储库的问题部分发布示例代码问题。
    猜你喜欢
    • 2021-11-19
    • 2020-09-01
    • 2019-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多