Flutter集成Android WebView遇摄像头、麦克风、位置权限拒绝问题
问题:Flutter WebView集成网页视频聊天应用时权限被拒绝(Access Denied)
开发的网页视频聊天应用在Chrome、Firefox等浏览器中运行正常,但集成到Android WebView后,即使已授予摄像头、麦克风和位置权限,打开应用仍出现“Access Denied”错误。已尝试更新AndroidManifest.xml文件并使用permission_handler包,问题依旧。
环境依赖
webview_flutter: ^4.10.0 webview_flutter_android: ^4.0.1 webview_flutter_wkwebview: ^3.16.1 webview_flutter_web: ^0.2.3+3 permission_handler: ^11.3.1
已尝试的配置与代码
AndroidManifest.xml
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-feature android:name="android.hardware.location.gps" /> <uses-feature android:name="android.hardware.location" /> <uses-feature android:name="android.hardware.camera" android:required="true" /> <activity> ... <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <intent-filter android:label="Deep Link"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:host="profile" android:scheme="scan" /> </intent-filter> <intent-filter android:label="App Links"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:host="profile" android:scheme="https" /> </intent-filter> </activity> <activity android:name="io.flutter.embedding.android.FlutterActivity"> <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" android:value="true" /> </activity>
WebView.dart
import 'package:flutter/material.dart'; import 'package:vc/utils/app_state.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:vc/utils/WebViewController.dart'; class WebViewPage extends StatefulWidget { final String url; const WebViewPage({Key? key, required this.url}) : super(key: key); @override State<WebViewPage> createState() => _WebViewPageState(); } class _WebViewPageState extends State<WebViewPage> { late WebViewController _controller; final String redirectUrl = "vc://thankyou"; @override void initState() { super.initState(); _initializeWebView(); } Future<void> _initializeWebView() async { try { final controller = await CustomWebViewController.createController( url: widget.url, redirectUrl: redirectUrl, onRedirect: _handleRedirection, ); setState(() { _controller = controller; }); } catch (e) { debugPrint('Error initializing WebViewController: $e'); } } void _handleRedirection(String url) { debugPrint("Redirecting to: $url"); final uniqueID = AppState().uniqueID ?? ''; Navigator.pop(context); if (uniqueID.isNotEmpty) { Navigator.pushNamed(context, '/thankyou', arguments: uniqueID); } else { Navigator.pushNamed(context, '/thankyou'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("CallPage"), ), body: _controller == null ? const Center(child: CircularProgressIndicator()) : WebViewWidget(controller: _controller), ); } }
WebViewController.dart
import 'package:permission_handler/permission_handler.dart'; import 'package:webview_flutter/webview_flutter.dart'; class CustomWebViewController { static Future<WebViewController> createController({ required String url, required String redirectUrl, required Function(String) onRedirect, }) async { await _requestCameraPermission(); final controller = WebViewController(); await controller.setJavaScriptMode(JavaScriptMode.unrestricted); await controller.setNavigationDelegate( NavigationDelegate( onPageStarted: (String currentUrl) { if (currentUrl.startsWith(redirectUrl)) { onRedirect(currentUrl); } }, ), ); await controller.loadRequest(Uri.parse(url)); return controller; } static Future<void> _requestCameraPermission() async { final status = await Permission.camera.request(); if (status.isGranted) { print('Camera permission granted'); } else if (status.isPermanentlyDenied) { print('Camera permission permanently denied'); await openAppSettings(); } else { print('Camera permission denied'); } } }
解决步骤
1. 补充请求所有必要权限
当前仅请求了摄像头权限,需要同时请求麦克风和位置权限,修改权限请求方法:
static Future<void> _requestAllPermissions() async { // 批量请求摄像头、麦克风、位置权限 final statuses = await [ Permission.camera, Permission.microphone, Permission.location, ].request(); // 输出各权限状态 statuses.forEach((permission, status) { if (status.isGranted) { print('${permission.name} permission granted'); } else if (status.isPermanentlyDenied) { print('${permission.name} permission permanently denied'); openAppSettings(); } else { print('${permission.name} permission denied'); } }); }
在createController方法中调用_requestAllPermissions()替代原_requestCameraPermission()。
2. 配置Android WebView媒体权限策略
添加Android平台专属配置,允许WebView自动授予媒体权限:
import 'package:webview_flutter_android/webview_flutter_android.dart'; // 在createController方法的controller初始化后添加: if (controller.platform is AndroidWebViewController) { final androidController = controller.platform as AndroidWebViewController; // 允许无需用户手势即可播放媒体 await androidController.setMediaPlaybackRequiresUserGesture(false); // 处理WebView权限请求 await androidController.setPermissionRequestHandler( (WebViewPermissionRequest request) { request.grant(request.resources); }, ); }
3. 完善AndroidManifest.xml配置
补充位置权限并启用硬件加速:
<!-- 添加精确/粗略位置权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 在主Activity标签内启用硬件加速 --> <activity ... android:hardwareAccelerated="true"> ... </activity>
4. 处理WebView权限请求回调
在NavigationDelegate中添加权限请求处理逻辑:
await controller.setNavigationDelegate( NavigationDelegate( onPageStarted: (String currentUrl) { if (currentUrl.startsWith(redirectUrl)) { onRedirect(currentUrl); } }, // 添加权限请求处理 onPermissionRequest: (PermissionRequest request) { request.grant(request.resources); }, ), );
内容的提问来源于stack exchange,提问作者user2806353
相关产品推荐
相关产品推荐

