请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

只点击一次退出app返回,直接退出app。H5也是只点击一次,直接是暴力退出app

1、app物理上点击返回直接退出app问题,并没有返回到首页问题。
图片描述
2、点击H5直接是暴力退出,并没有返回到首页问题。
图片描述

源码:
hi_webview.dart

import 'package:flutter/material.dart';
import 'package:trip/util/navigator_util.dart';
import 'package:webview_flutter/webview_flutter.dart';

///H5容器
class HiWebView extends StatefulWidget {
  final String? url;
  final String? statusBarColor;
  final String? title;
  final bool? hideAppBar;

  ///禁止我的页面返回按钮
  final bool? backForbid;

  const HiWebView(
      {super.key,
        this.url,
        this.statusBarColor,
        this.title,
        this.hideAppBar,
        this.backForbid});

  @override
  State<HiWebView> createState() => _HiWebViewState();
}

class _HiWebViewState extends State<HiWebView> {
  ///主页代表的url
  final _catchUrls = [
    'm.ctrip.com/',
    'm.ctrip.com/html5/',
    'm.ctrip.com/html5'
  ];
  String? url;
  late WebViewController controller;

  @override
  void initState() {
    super.initState();
    url = widget.url;
    if (url != null && url!.contains('ctrip.com')) {
      //fix 携程H5 http://无法打开问题
      url = url!.replaceAll("http://", "https://");
    }
    _initWebViewController();
  }

  @override
  Widget build(BuildContext context) {
    String statusBarColorStr = widget.statusBarColor ?? 'ffffff';
    Color backButtonColor;
    if (statusBarColorStr == 'ffffff') {
      backButtonColor = Colors.black;
    } else {
      backButtonColor = Colors.white;
    }
    //处理Android物理返回键,返回H5的上一页 https://docs.flutter.dev/release/breaking-changes/android-predictive-back
    return PopScope(
        canPop: false,
        onPopInvoked: (bool didPop) async {
          if (await controller.canGoBack()) {
            //返回H5的上一页
            controller.goBack();
          } else {
            if (context.mounted) NavigatorUtil.pop(context);
          }
        },
        child: Scaffold(
          body: Column(
            children: [
              _appBar(
                  Color(int.parse('0xff$statusBarColorStr')), backButtonColor),
              Expanded(
                  child: WebViewWidget(
                    controller: controller,
                  ))
            ],
          ),
        ));
  }

  void _initWebViewController() {
    controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setBackgroundColor(const Color(0x00000000))
      ..setNavigationDelegate(NavigationDelegate(
          onProgress: (int progress) {
            debugPrint('progress:$progress');
          },
          onPageStarted: (String url) {},
          onPageFinished: (String url) {
            //页面加载完成之后才能执行JS
            _handleBackForbid();
          },
          onWebResourceError: (WebResourceError error) {},
          onNavigationRequest: (NavigationRequest request) {
            if (_isToMain(request.url)) {
              debugPrint('阻止跳转到 $request}');
              //返回到flutter页面
              NavigatorUtil.pop(context);
              return NavigationDecision.prevent;
            }
            debugPrint('允许跳转到 $request}');
            return NavigationDecision.navigate;
          }))
      ..loadRequest(Uri.parse(url!));
  }

  ///隐藏H5登录页的返回键
  ///实现思路:
  /// 1. 通过观察H5我的页面的返回按钮的样式为.animationComponent.rn-view ;
  /// 2. 所以通过查找页面中具有类名 .animationComponent.rn-view 的元素,然后通过将它的style.display 设置为 'none'来隐藏这个元素;
  /// 另外:如果想隐藏的H5返回键不支持怎么办?
  /// 可以按照上面因此H5我的页面的思路,在Chrome上打开这个H5页面,然后通过开发者模式找到通过查找页面中的返回按钮的样式class的元素,然后通过将它的style.display 设置为 'none'来隐藏这个元素;
  void _handleBackForbid() {
    const jsStr =
        "var element = document.querySelector('.animationComponent.rn-view'); if(element != null) element.style.display = 'none';";
    if (widget.backForbid ?? false) {
      controller.runJavaScript(jsStr);
    }
  }

  ///判断H5是否返回主页
  bool _isToMain(String? url) {
    bool contain = false;
    for (final value in _catchUrls) {
      if (url?.endsWith(value) ?? false) {
        contain = true;
        break;
      }
    }
    return contain;
  }

  _appBar(Color backgroundColor, Color backButtonColor) {
    //获取刘海屏Top安全边距
    double top = MediaQuery.of(context).padding.top;
    if (widget.hideAppBar ?? false) {
      return Container(
        color: backgroundColor,
        height: top,
      );
    }
    return Container(
      color: backgroundColor,
      padding: EdgeInsets.fromLTRB(0, top, 0, 10),
      child: FractionallySizedBox(
        widthFactor: 1,
        child: Stack(
          children: [_backButton(backButtonColor), _title(backButtonColor)],
        ),
      ),
    );
  }

  _backButton(Color backButtonColor) {
    return GestureDetector(
      onTap: () {
        NavigatorUtil.pop(context);
      },
      child: Container(
        margin: const EdgeInsets.only(left: 10),
        child: Icon(
          Icons.close,
          color: backButtonColor,
          size: 26,
        ),
      ),
    );
  }

  _title(Color backButtonColor) {
    return Positioned(
        left: 0,
        right: 0,
        child: Center(
          child: Text(
            widget.title ?? "",
            style: TextStyle(color: backButtonColor, fontSize: 20),
          ),
        ));
  }
}

源码:
navigator_util.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:trip/page/home_page.dart';
import 'package:trip/page/login_page.dart';
import 'package:trip/widget/hi_webview.dart';

import '../navigator/tab_navigator.dart';

class NavigatorUtil {
  // 用于在获取不到context的地方,如dao种跳转页面时使用,需要在HomePage赋值
  // 注意: 若TabNavigator被销毁,_context将无法使用
  static BuildContext? _context;

  static updateContext(BuildContext context) {
    NavigatorUtil._context = context;
  }

  // 跳转到指定页面
  static push(BuildContext context, Widget page) {
    Navigator.push(context, MaterialPageRoute(builder: (context) => page));
  }

  // 跳转到首页
  static goToHome(BuildContext context) {
    //跳转到主页并不让返回
    Navigator.pushReplacement(
        // context, MaterialPageRoute(builder: (context) => const HomePage()));
        context,
        MaterialPageRoute(builder: (context) => const TabNavigator()));
  }

  // 跳转登录
  static goToLogin() {
    //跳转到登录并不让返回
    Navigator.pushReplacement(
        _context!, MaterialPageRoute(builder: (context) => const LoginPage()));
  }

  // 返回上一页
  static pop(BuildContext context) {
    if (Navigator.canPop(context)) {
      Navigator.pop(context);
    } else {
      // 退出App
      SystemNavigator.pop();
    }
  }

  // 跳转H5页面
  static jumpH5(
      {BuildContext? context,
      required String url,
      String? title,
      bool? hideAppBar,
      String? statusBarColor}) {
    BuildContext? safeContext;
    if (context != null) {
      safeContext = context;
    } else if (_context?.mounted ?? false) {
      safeContext = _context;
    } else {
      debugPrint('context is null jumpH5 failed.');
      return;
    }
    Navigator.push(
      safeContext!,
      MaterialPageRoute(
        builder: (context) => HiWebView(
          url: url,
          title: title,
          hideAppBar: hideAppBar,
          statusBarColor: statusBarColor,
        ),
      ),
    );
  }
}

正在回答 回答被采纳积分+3

1回答

CrazyCodeBoy 2024-10-10 21:18:34
扩展下方法添加个参数,如果是webview调用不执行SystemNavigator.pop();就可以了。

以下是修改后的代码:

// 返回上一页
static pop(BuildContext context, {bool isWebView = false}) {
  if (Navigator.canPop(context)) {
    Navigator.pop(context);
  } else {
    if (!isWebView) {
      // 退出App
      SystemNavigator.pop();
    } else {
      debugPrint('WebView页面,不执行SystemNavigator.pop');
    }
  }
}

新增的 isWebView 参数解释:

isWebView:当它为 true 时,表示当前是 WebView 页面,不退出应用,只处理页面返回;当它为 false(默认值)时,允许退出应用。


你可以在调用 NavigatorUtil.pop() 的地方传入这个参数。例如,在 WebView 页面的返回按钮处理时:

NavigatorUtil.pop(context, isWebView: true);

这样,当用户在 WebView 页面点击返回按钮时,应用不会退出;但在非 WebView 页面时,仍然可以正常退出应用。

0 回复 有任何疑惑可以回复我~
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信