webview_flutter

这篇具有很好参考价值的文章主要介绍了webview_flutter。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

查看webview内核

​https://liulanmi.com/labs/core.html​

h5中获取设备

https://cloud.tencent.com/developer/ask/sof/105938013

https://developer.mozilla.org/zh-CN/docs/Web/API/Navigator/mediaDevices

web资源部署后navigator获取不到mediaDevices实例的解决方案(navigator.mediaDevices为undefined)_navigator.mediadevices不存在_乐辞的博客-CSDN博客

自已遇到的坑:

需求是app进入webview 使用h5网页获取摄像头进行人脸识别。但是h5那边一直获取不到摄像头权限

H5 页面通过navigator.mediaDevices.getUserMedia调用手机摄像头拍照上传_navigator.webkitgetusermedia-CSDN博客

web资源部署后navigator获取不到mediaDevices实例的解决方案(navigator.mediaDevices为undefined)_navigator.mediadevices不存在_乐辞的博客-CSDN博客

h5 navigator.mediaDevices 一直是undefined 

原因1: 由于浏览器的安全策略导致了这个问题,目前经尝试,在以下几种情况中 navigator.mediaDevices 可以正常使用 

1. 地址为localhost:// 访问时 2. 地址为https:// 时 3. 为文件访问file:///

原因2:排除上面的问题, 使用了navigator兼容性写法获取 getUserMedia 摄像头设备 但是 getUserMedia 依旧为underfined--》怀疑是app的webview 的问题

怀疑是webview版本问题

解决方式1: 我将webview升级到最新版 发现问题不是webview的版本问题❌

最后发现flutter  app中 申请权限 使用   permission_handler: ^10.3.0

await Permission.camera.request() ->

if (await KPermiseeUtil.checkAndDoDefault(
   Permission.camera)) {
    _callCamera();
 }

安卓 谷歌内核:-》方向权限在安卓声明文件  AndroidManifest.xml 什么了对应权限 app内权限是有的。那说明只是webview的权限问题

安卓权限配置 和webview权限配置

android - How to access the camera from within a Webview? - Stack Overflow

按照上面的 依旧没有解决

最后发现 webview有个权限申请   

controller.platform.setOnPlatformPermissionRequest

虽然app 进入webview申请了权限 方式 webview内部也需要进行权限申请 

默认setOnPlatformPermissionRequest 这个函数回调是拒绝的 所以加下面的配置 解决

安卓不能使用h5打开摄像头的问题。

controller.platform.setOnPlatformPermissionRequest(
      (request) {
        request.grant();
      },
    );

苹果 wkwebview的内核 

如果开始有权限 流程正常,如果开始app没有权限 申请权限后 webview 依旧没有权限 需要退出app重新进才会有权限

原因解决:

之前在ios的info.plist 中声明的是这样的

	<key>NSCameraUsageDescription</key>
	<string></string>

app内申请权限 依旧可以正常使用 并获取到,但是webview不行

<key>NSCameraUsageDescription</key>
<string>Konnect wants to use your camera, is that allowed?</string>

原因是<string></string> =》

对flutter app请求权限做了如下封装<string>Konnect wants to use your camera, is that allowed?</string>

权限虽然声明了 但是,没有说明权限的使用用途,这个描述提示会 展示在app申请权限的弹窗底部

加上了 就解决了 上面的问题:

注意:

ios权限声明 必须添加描述 权限使用来干什么  不然app上架会被拒绝。

NSCameraUsageDescription_camera usage d-CSDN博客

import 'dart:io';

import 'package:app/common/util/k_log_util.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';

class KPermiseeUtil {
  static final bool isIos = Platform.isIOS;
  static final bool isAndroid = Platform.isAndroid;

  // 检查权限 并做相应的处理
  static Future<bool> checkAndDoDefault(Permission permission) async {
    final status = await permission.status;
    KLogUtil.log(["status", status]);
    if (isIos) {
      switch (status) {
        case PermissionStatus.granted:
          return true;
        case PermissionStatus.permanentlyDenied:
          openAppSettings();
          return false;
        case PermissionStatus.denied:
        case PermissionStatus.limited:
        case PermissionStatus.restricted:
        default:
          final newStatus = await permission.request();
          if (newStatus == PermissionStatus.granted) {
            return true;
          }
          return false;
      }
    } else if (isAndroid) {
      switch (status) {
        case PermissionStatus.granted:
          return true;
        default:
          final newStatus = await permission.request();
          KLogUtil.log(["newStatus", newStatus]);
          if (newStatus == PermissionStatus.granted) {
            return true;
          } else if (newStatus == PermissionStatus.denied) {
            return false;
          } else if (newStatus == PermissionStatus.permanentlyDenied) {
            openAppSettings();
            return false;
          }
          return false;
      }
    }
    return await permission.status == PermissionStatus.granted;
  }

  static Future<bool> checkStatusAndDoDefault(
      PermissionState status, Permission permission) async {
    KLogUtil.log(["status", status]);
    if (isIos) {
      switch (status) {
        // notDetermined 未设置授权
        case PermissionState.notDetermined:
          return true;

        // 该应用程序未被授权访问照片库,用户也无法授予此类权限。
        case PermissionState.restricted:
          openAppSettings();
          return false;
        // 用户明确拒绝此应用程序访问照片库。
        case PermissionState.denied:
        // 用户明确授予此应用程序访问照片库的权限。
        case PermissionState.authorized:
        case PermissionState.limited:
        default:
          final newStatus = await permission.request();
          if (newStatus == PermissionStatus.granted) {
            return true;
          }
          return false;
      }
    } else if (isAndroid) {
      switch (status) {
        case PermissionStatus.granted:
          return true;
        default:
          final newStatus = await permission.request();
          KLogUtil.log(["newStatus", newStatus]);
          if (newStatus == PermissionStatus.granted) {
            return true;
          } else if (newStatus == PermissionStatus.denied) {
            return false;
          } else if (newStatus == PermissionStatus.permanentlyDenied) {
            openAppSettings();
            return false;
          }
          return false;
      }
    }
    return await permission.status == PermissionStatus.granted;
  }
}

最后自己的封装如下 (最新版本webview)

  webview_flutter: ^4.2.4

  webview_flutter_android: ^3.10.1

  webview_flutter_wkwebview: ^3.7.4文章来源地址https://www.toymoban.com/news/detail-730774.html

import 'dart:convert';
import 'dart:io';

import 'package:app/common/theme/app_theme.dart';
import 'package:app/common/util/k_log_util.dart';
import 'package:app/entity/wallet/wallet_entity.dart';
import 'package:app/gen/assets.gen.dart';
import 'package:app/pages/widget/placeholder_widget.dart';
import 'package:app/pages/widget/top_appbar.dart';
import 'package:app/sql/wallet_sql/wallet_sql.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:webview_flutter/webview_flutter.dart';

import 'placeholder_type.dart';
import 'package:webview_flutter_android/webview_flutter_android.dart';
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';

// ignore: must_be_immutable
class CommonWebView extends StatefulWidget {
  final String title;
  final String url;
  bool isHiddenBar;
  CommonWebView({
    super.key,
    required this.title,
    required this.url,
    this.isHiddenBar = false,
  });

  @override
  State<CommonWebView> createState() => _CommonWebViewState();
}

class _CommonWebViewState extends State<CommonWebView> {
  late double progress = 0.01; //H5加载进度值
  final double VISIBLE = 2;
  final double GONE = 0;
  late double progressHeight = GONE; //H5加载进度条高度
  late WebViewController controller;
  late final PlatformWebViewControllerCreationParams params;
  @override
  void initState() {
    webViewInit();
    super.initState();
  }

  webViewInit() {
    if (WebViewPlatform.instance is WebKitWebViewPlatform) {
      params = WebKitWebViewControllerCreationParams(
        allowsInlineMediaPlayback: true,
        mediaTypesRequiringUserAction: const <PlaybackMediaTypes>{},
      );
    } else {
      params = const PlatformWebViewControllerCreationParams();
    }

    controller = WebViewController.fromPlatformCreationParams(params);
    if (controller.platform is AndroidWebViewController) {
      AndroidWebViewController.enableDebugging(true);
      (controller.platform as AndroidWebViewController)
          .setMediaPlaybackRequiresUserGesture(false);
    }
    controller.platform.setOnPlatformPermissionRequest(
      (request) {
        request.grant();
      },
    );
    controller
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..addJavaScriptChannel("konnect", onMessageReceived: onMessageReceived)
      ..setBackgroundColor(AppTheme.themeColor_black)
      ..enableZoom(true)
      ..setNavigationDelegate(
        NavigationDelegate(
          onProgress: (int progress) {
            // Update loading bar.
            //加载H5页面时触发多次,progress值为0-100
            this.progress = progress.toDouble() / 100.0; //计算成0.0-1.0之间的值
          },
          onPageStarted: (String url) {
            //H5页面开始加载时触发
            setProgressVisible(GONE); //VISIBLE 显示加载进度条
          },
          onPageFinished: (String url) {
            //H5页面加载完成时触发
            setProgressVisible(GONE); //隐藏加载进度条
          },
          onWebResourceError: (WebResourceError error) {
            KLogUtil.log(["error", error]);
            if (error.isForMainFrame == true) {
              switch (error.errorType) {
                case WebResourceErrorType.badUrl:
                case WebResourceErrorType.timeout:
                  break;
                case WebResourceErrorType.hostLookup:
                  Get.off(() => const PageError404());
                default:
                  break;
              }
            }
          },
          onNavigationRequest: (NavigationRequest request) {
            if (request.url.startsWith('https://www.youtube.com/')) {
              return NavigationDecision.prevent;
            }
            return NavigationDecision.navigate;
          },
        ),
      )
      ..loadRequest(Uri.parse(widget.url));
  }

  //显示或隐藏进度条
  void setProgressVisible(double isVisible) {
    setState(() {
      progressHeight = isVisible;
    });
  }

  // 接受h5发送来的数据
  onMessageReceived(message) async {
    //接收H5发过来的数据
    String sendMesStr = message.message;
    print("H5发过来的数据1: $sendMesStr");
    KLogUtil.log(["H5发过来的数据1", sendMesStr]);
    Map<String, dynamic> msg = json.decode(sendMesStr);
    int type = msg["type"] ?? -1;
    String method = msg["method"] ?? "";
    Map<String, dynamic> data = msg["data"] ?? {};
    if (type != -1) {
      switch (type) {
        case 1:
          break;
        case 2:
          break;
        case 3:
        default:
          break;
      }
    }

    if (method.isNotEmpty) {
      switch (method) {
        case "back":
          KLogUtil.log(["back", data, msg]);
          Get.back<Map<String, dynamic>>(result: data);

          break;
        case "getWalletAddress":
          WalletEntity? myWallet = await WalletSql.getDefaultWallet();
          if (myWallet != null) {
            String walletAddress = myWallet.walletAddress;
            String signature = myWallet.signature ?? "";
            controller.runJavaScript(
                "appCallMethod('walletAddress','$walletAddress')");
            controller.runJavaScript("appCallMethod('signature','$signature')");
            KLogUtil.log(["获取到钱包", walletAddress, signature]);
          } else {
            KLogUtil.log(["没有获取到钱包"]);
          }
        default:
          break;
      }
    }
  }

  backPress() async {
    Get.back();
    return;
    // 有问题
    // String? cur = await _webControl.currentUrl();
    // KLogUtil.log(["backPress", initUrl, cur]);
    // if (initUrl == cur) {
    //   // 最后一页
    //   KLogUtil.log(["最后一页"]);
    //   Get.back();
    // }
    // if (await _webControl.canGoBack()) {
    //   _webControl.goBack(); //返回上个网页
    //   return false;
    // }
    // Get.back();
  }

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        backgroundColor: AppTheme.themeColor_black,
        appBar: widget.isHiddenBar
            ? null
            : WebViewTopAppBar(title: widget.title, backPress: backPress),
        body: WebViewWidget(controller: controller),
      ),
    );
  }
}

class PageError404 extends StatefulWidget {
  const PageError404({super.key});

  @override
  State<PageError404> createState() => _PageError404State();
}

class WebViewTopAppBar extends PreferredSize {
  WebViewTopAppBar({
    key,
    required String title, //标题
    final Color? titleColor, //title颜色
    final Color? backgroundColor, //导航栏背景颜色
    final Widget? leadWidget, //左边按钮
    final PreferredSizeWidget? bottom, //底部组件
    final double? elevation, //AppBar底部阴影效果,0为无阴影。
    final bool statusBarColor = true, //控制状态栏的颜色true为灰色,false为白色
    final List<Widget>? rightActions, //右边按钮组
    final double? preferredSize, //状态栏高度
    final double bottomHeight = 0.0, //appBar底部高度
    final TextStyle? titleStyle,
    Function()? backPress, //返回按钮回调函数
  }) : super(
          key: key,
          preferredSize: Size.fromHeight(44.h),
          child: AppBar(
            title: Text(
              title,
              style: titleStyle ??
                  TextStyle(
                    fontSize: 16.sp,
                    fontWeight: FontWeight.bold,
                    color: titleColor ?? Colors.white,
                  ),
            ),
            centerTitle: true,
            leading: leadWidget ??
                InkWell(
                  onTap: backPress,
                  child: Padding(
                    padding: EdgeInsets.all(6.r),
                    child: Assets.icon.arrowBack.image(),
                  ),
                ),
            actions: rightActions,
            bottom: bottom,
            elevation: elevation ?? 0,
            // titleTextStyle: Theme.of(context).textTheme.bodySmall,
            backgroundColor: backgroundColor ?? AppTheme.themeColor_black,
            systemOverlayStyle: SystemUiOverlayStyle(
              statusBarColor: Colors.transparent,
              statusBarBrightness:
                  Platform.isAndroid ? Brightness.light : Brightness.dark,
              statusBarIconBrightness:
                  Platform.isAndroid ? Brightness.light : Brightness.dark,
              systemNavigationBarIconBrightness: Brightness.light,
              systemNavigationBarColor: AppTheme.themeColor_black,
            ),
          ),
        );
}

class _PageError404State extends State<PageError404> {
  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        backgroundColor: AppTheme.themeColor_black,
        appBar: TopAppBar(context, "404"),
        body: PlaceholderWidget.emptyPlaceholder(
            placeholderType: PlaceholderType.notFound404),
      ),
    );
  }
}

到了这里,关于webview_flutter的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • flutter开发实战-webview插件flutter_inappwebview使用

    flutter开发实战-webview插件flutter_inappwebview使用 在开发过程中,经常遇到需要使用WebView,Webview需要调用原生的插件来实现。常见的flutter的webview插件是webview_flutter,flutter_inappwebview。之前整理了一下webview_flutter,查看https://blog.csdn.net/gloryFlow/article/details/131683122 这里我们使用fl

    2024年02月07日
    浏览(36)
  • flutter:webview_flutter和flutter_inappwebview的简单使用

    最近在研究如何在应用程序中嵌入Web视图,发现有两个库不错。 一个是官方维护、一个是第三方维护。因为没说特别的需求,就使用了官方库,实现一些简单功能是完全ok的 不建议使用,因为效果不怎么样,当然也可能是我太菜不会用,下面这个问题就很难理解为什么会这样

    2024年02月12日
    浏览(42)
  • flutter 封装webview和使用本地网页

    最先看到flutter_webview_plugin 用法特别简单 flutter_webview_plugin | Flutter Package Plugin that allow Flutter to communicate with a native Webview. https://pub-web.flutter-io.cn/packages/flutter_webview_plugin 缺点: 没有实现js sdk的功能 没有办法 使用JavaScriptChannel 的功能 后面使用webview_flutter webview_flutter | Flutter

    2024年03月26日
    浏览(44)
  • 在 Flutter 中使用 webview_flutter 4.0 | js 交互

    大家好,我是 17。 已经有很多关于 Flutter WebView 的文章了,为什么还要写一篇。两个原因: Flutter WebView 是 Flutter 开发的必备技能 现有的文章都是关于老版本的,新版本 4.x 有了重要变化,基于 3.x 的代码很多要重写。 WebView 的文章分两篇 在 Flutter 中使用 webview_flutter 4.0 | js

    2024年01月18日
    浏览(26)
  • flutter ios webview不能打开http地址

    参考 1、iOS添加信任 webview_flutter 在使用过程中会iOS出现无法加载HTTP请求的情况, 但是Flutter 却可以加载HTTP请求。这就与两个的框架有关了,Flutter是独立于UIKit框架的。 解决方案就是在iOS 的info.plist中添加对HTTP的信任。 安卓的一些地址打不开 Flutter-Webview组件处理Scheme协议

    2024年02月11日
    浏览(27)
  • Flutter中实现交互式Webview的方法

    前言: Flutter是一款强大的跨平台移动应用开发框架,而Webview则是在应用中展示Web内容的重要组件。本文将介绍如何在Flutter应用中实现交互式的Webview,以便为用户提供更加丰富的内容和功能。 1. 引入webview_flutter插件 要在Flutter应用中使用Webview,需要首先引入webview_flutter插件

    2024年02月09日
    浏览(57)
  • 我的第一个flutter项目(Android & Webview)

    前言:flutter开发环境搭建Flutter的开发环境搭建-图解_☆七年的博客-CSDN博客 第一个flutter简单项目,内容是一个主界面,其中:     1.内容点击数字自增     2.跳转一个空页,     3.跳转一个WebView界面 其中涉及添加主键,新建界面类,导入依赖,使用WebView, 兼容http网络安全

    2024年02月15日
    浏览(49)
  • flutter开发实战-webview自定义标题栏Appbar

    flutter开发实战-webview定义标题栏Appbar 在开发中,使用到webview,在之前实现webview使用,webview页面使用的时自定义标题栏,在上一个webview结合JsBridge实现交互忘记这个标题栏,这里记录一下。 flutter开发实战-webview定义标题栏Appbar,PreferredSizeWidget webview页面使用的时自定义标题

    2024年02月16日
    浏览(30)
  • flutter开发实战-实现webview与Javascript通信JSBridge

    flutter开发实战-实现webview与H5中Javascript通信JSBridge 在开发中,使用到webview,flutter实现webview是使用原生的插件实现,常用的有webview_flutter与flutter_inappwebview 这里使用的是webview_flutter,在iOS上,WebView小部件由WKWebView支持。在Android上,WebView小部件由WebView支持。 这里使用的是w

    2024年02月14日
    浏览(31)
  • 跨端的三种方案原理和对比(WebView,ReactNative,Flutter)

    WebView是什么?  答: 第一代跨平台框架,代表者为:PhoneGap、微信小程序。 WebView标签是一种用于在网页中嵌入浏览器窗口的HTML元素。它的底层原理是通过 原生平台提供的浏览器引擎 来实现网页的渲染和交互。 在Android平台上,WebView使用的是Android系统提供的WebView组件,它

    2024年02月05日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包