【问题标题】:Using flutter webview as home and pressing back button closes Application使用flutter webview作为主页并按下后退按钮关闭应用程序
【发布时间】:2020-01-12 16:32:12
【问题描述】:

我正在尝试在 Flutter 中使用 webview 作为我的应用程序的主页。一切都加载得很好,但是点击后退按钮不会将我带到 web 视图中的上一个网页,它只是退出应用程序。

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:url_launcher/url_launcher.dart';

class _StudentPortalState extends State<StudentPortal>{

  _launchURL(url) async {

  if (await canLaunch(url)) {
    await launch(url);
  } else {
    throw 'Could not launch $url';
  }
}
  Widget build (BuildContext context){
    return Scaffold(
      body: WebView(
              javascriptMode: JavascriptMode.unrestricted,
              initialUrl: 'https://sites.google.com/ttmsa.org/ttmsa/home',

              navigationDelegate: (NavigationRequest request) {

                if(request.url.contains("intent:")) {

                  _launchURL('https://forms.gle/XfNbn1Ph9xFdUaKeA');
                  return NavigationDecision.prevent;
                }
                return NavigationDecision.navigate;
              },   
      ),

    );
  }
}`

【问题讨论】:

  • 我认为这是使用 webview 插件时的预期行为,除非您知道任何文档另有说明。
  • 你试过使用 WebViewController 类吗?使用 canGoBack 方法检查是否有返回历史记录,然后使用 goBack 方法返回上一页。
  • @FarhanSyah 好主意,我会查看 webview 控制器,看看是否存在返回方法,谢谢

标签: flutter webview flutter-dependencies


【解决方案1】:

onbackpressed Webview 的代码

处理后退按钮的未来

    WebViewController controllerGlobal;

Future<bool> browserBack(BuildContext context) async{
  print('activated');
 if (await controllerGlobal.canGoBack()) {
    Scaffold.of(context).showSnackBar(
      const SnackBar(content: Text("Munching....")),
    );
    print("onwill goback");
    controllerGlobal.goBack();
  }
 else {
    SystemChannels.platform.invokeMethod('SystemNavigator.pop');
 return Future.value(false);
  }
}

WillPopScope 小部件

Widget build(BuildContext context) {


    return WillPopScope(
      onWillPop: ()=> browserBack(context),
      child:Scaffold(

      body: Builder(builder: (BuildContext context){
          return WebView(
            initialUrl: 'https://flutter.dev/docs/deployment/android',
            javascriptMode: JavascriptMode.unrestricted,
            onWebViewCreated: (WebViewController webViewController) {
            //  _controller.complete(webViewController);
              controllerGlobal = webViewController;
            },
          );
      },),

      ),
    );

【讨论】:

  • _controller 来自哪里?是的,它不存在。删除这个
  • "返回 Future.value(false);"应该在 if/else 语句之外
【解决方案2】:

完美运行的代码

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:webview_flutter/webview_flutter.dart';

class Homepage extends StatefulWidget {
  @override
  HomepageState createState() => HomepageState();
}

class HomepageState extends State<Homepage> {
  WebViewController _controller;

  final Completer<WebViewController> _controllerCompleter =
      Completer<WebViewController>();

  @override
  void initState() {
    super.initState();
    // Enable hybrid composition.
    if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () => _goBack(context),
      child: WebView(
        initialUrl: 'https://google.com',
        javascriptMode: JavascriptMode.unrestricted,
        onWebViewCreated: (WebViewController webViewController) {
          _controllerCompleter.future.then((value) => _controller = value);
          _controllerCompleter.complete(webViewController);
        },
      ),
    );
  }

  Future<bool> _goBack(BuildContext context) async {
    if (await _controller.canGoBack()) {
      _controller.goBack();
      return Future.value(false);
    } else {
      showDialog(
          context: context,
          builder: (context) => AlertDialog(
                title: Text('Do you want to exit'),
                actions: <Widget>[
                  FlatButton(
                    onPressed: () {
                      Navigator.of(context).pop();
                    },
                    child: Text('No'),
                  ),
                  FlatButton(
                    onPressed: () {
                      SystemNavigator.pop();
                    },
                    child: Text('Yes'),
                  ),
                ],
              ));
      return Future.value(true);
    }
  }
}

【讨论】:

  • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。
【解决方案3】:

您必须控制 WillPopScope 组件中的逻辑:

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'dart:async';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Home', url: 'https://flutter.dev/'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title, this.url});

  final String title;
  final String url;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  WebViewController _controller;

  final Completer<WebViewController> _controllerCompleter =
      Completer<WebViewController>();
  //Make sure this function return Future<bool> otherwise you will get an error
  Future<bool> _onWillPop(BuildContext context) async {
    if (await _controller.canGoBack()) {
      _controller.goBack();
      return Future.value(false);
    } else {
      return Future.value(true);
    }
  }

@override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () => _onWillPop(context),
      child: Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: SafeArea(
            child: WebView(
          key: UniqueKey(),
          onWebViewCreated: (WebViewController webViewController) {
            _controllerCompleter.future.then((value) => _controller = value);
            _controllerCompleter.complete(webViewController);
          },
          javascriptMode: JavascriptMode.unrestricted,
          initialUrl: widget.url,
        )),
      ),
    );
  }
}

【讨论】:

    【解决方案4】:

    这是最好的答案(100% 工作)-

      // Use any webview plugin
      // WebView _controller;
    
      Future<void> _handleBack(context) async {
        var status = await _controller.canGoBack();
        if (status) {
          _controller.goBack();
        } else {
          showDialog(
              context: context,
              builder: (context) => AlertDialog(
                    title: Text('Do you want to exit'),
                    actions: <Widget>[
                      FlatButton(
                        onPressed: () {
                          Navigator.of(context).pop();
                        },
                        child: Text('No'),
                      ),
                      FlatButton(
                        onPressed: () {
                          SystemNavigator.pop();
                        },
                        child: Text('Yes'),
                      ),
                    ],
                  ));
        }
      }
    

    这里是调用 WillPopScope

      @override
      Widget build(BuildContext context) {
        return WillPopScope(
                onWillPop: () => _handleBack(context),
                child: SafeArea(
                  top: true,
                  child: Scaffold(......)))
    

    【讨论】:

      【解决方案5】:

      用 WillPopScope 包裹 Scaffold 并在用户单击设备返回按钮时执行 WebView Controller goback

      code sn-p onwillpop

          @override
        Widget build(BuildContext context) {
          return WillPopScope(
            onWillPop: () => _exitApp(context),
            child: 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),
                ],
              ),
      

      退出应用的代码 sn-p

      WebViewController controllerGlobal;
      
      Future<bool> _exitApp(BuildContext context) async {
        if (await controllerGlobal.canGoBack()) {
          print("onwill goback");
          controllerGlobal.goBack();
          return Future.value(true);
        } else {
          Scaffold.of(context).showSnackBar(
            const SnackBar(content: Text("No back history item")),
          );
          return Future.value(false);
        }
      }
      

      完整代码

      // 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><a href="https://www.google.com/">https://nodejs.org/en</a></ul>
      </ul>
      </body>
      </html>
      ''';
      
      class WebViewExample extends StatefulWidget {
        @override
        _WebViewExampleState createState() => _WebViewExampleState();
      }
      
      WebViewController controllerGlobal;
      
      Future<bool> _exitApp(BuildContext context) async {
        if (await controllerGlobal.canGoBack()) {
          print("onwill goback");
          controllerGlobal.goBack();
        } else {
          Scaffold.of(context).showSnackBar(
            const SnackBar(content: Text("No back history item")),
          );
          return Future.value(false);
        }
      }
      
      class _WebViewExampleState extends State<WebViewExample> {
        final Completer<WebViewController> _controller =
        Completer<WebViewController>();
      
        @override
        Widget build(BuildContext context) {
          return WillPopScope(
            onWillPop: () => _exitApp(context),
            child: 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;
                    }
                    if (request.url.startsWith('https://flutter.dev/docs')) {
                      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;
              controllerGlobal = controller;
      
              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();
                    },
                  ),
                ],
              );
            },
          );
        }
      }
      

      工作演示。我单击桌面页面,然后按设备返回按钮,您可以看到信息显示“onwill goback”。并返回上一页

      【讨论】:

      • pub.dev/packages/minimize_app 调用比Scaffold.of(context).showSnackBar(const SnackBar(content: Text("No back history item")),); 有用
      • @Yash 不是更好的答案。不使用 inappwebview
      • 我想隐藏 appBar: AppBar.如果我删除 appBar - 后退导航不起作用。我该怎么办?
      • _exitApp()的返回值是不是顺序不对?如果应用程序应该返回而不是 webview,它不应该返回 true 吗?
      • 这项工作!非常感谢!
      猜你喜欢
      • 1970-01-01
      • 2016-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-08
      • 2020-04-13
      • 1970-01-01
      相关资源
      最近更新 更多