我创建了一个网络视频聊天应用程序,在拨打电话之前需要获得访问摄像头、麦克风和位置的权限。该应用程序在 Chrome 和 Firefox 等浏览器中运行良好。问题是我正在尝试使用 Android WebView 将此 Web 应用程序集成到 Android 应用程序中。即使在 Web 视图中授予相机、麦克风和位置的权限后,我在打开 Web 应用程序时仍收到“访问被拒绝”错误。我尝试更新 AndroidManifest.xml 文件并使用permission_handler 包,但错误仍然存在。我是 Flutter 新手,希望能帮助解决这个问题。
pubspec.yaml webview_flutter:^4.10.0 webview_flutter_android:^4.0.1 webview_flutter_wkwebview:^3.16.1 webview_flutter_web:^0.2.3+3 权限处理程序:^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');
}
}
}
您似乎已经做了很多正确的事情,将摄像头、麦克风和位置权限与 Flutter 应用程序中的 WebView 集成,但有几个关键点需要解决。以下是解决该问题的一些可能的解决方案和建议:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
<uses-feature android:name="android.hardware.microphone" android:required="true" />
您需要在WebView设置中启用权限请求并 使用 WebViewClient 处理它们。
WebViewController.dart
import 'package:permission_handler/permission_handler.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:flutter/services.dart';
class CustomWebViewController {
static Future<WebViewController> createController({
required String url,
required String redirectUrl,
required Function(String) onRedirect,
}) async {
await _requestPermissions();
final controller = WebViewController();
await controller.setJavaScriptMode(JavaScriptMode.unrestricted);
await controller.setNavigationDelegate(
NavigationDelegate(
onPageStarted: (String currentUrl) {
if (currentUrl.startsWith(redirectUrl)) {
onRedirect(currentUrl);
}
},
),
);
// Enable media permissions (camera, microphone, etc.) in WebView
await controller.setMediaPlaybackRequiresUserGesture(false);
await controller.setAllowUniversalAccessFromFileURLs(true);
// Handle permission requests from the WebView
await controller.setPermissionRequestHandler(
(controller, request) async {
// Automatically accept camera and microphone requests
if (request.origin == 'https://your-web-app-url.com') {
if (request.permissions.contains(Permission.camera)) {
controller.grantPermission(Permission.camera);
}
if (request.permissions.contains(Permission.microphone)) {
controller.grantPermission(Permission.microphone);
}
}
return true;
}
);
await controller.loadRequest(Uri.parse(url));
return controller;
}
static Future<void> _requestPermissions() async {
final statusCamera = await Permission.camera.request();
final statusMicrophone = await Permission.microphone.request();
final statusLocation = await Permission.location.request();
if (statusCamera.isGranted && statusMicrophone.isGranted && statusLocation.isGranted) {
print('Permissions granted');
} else {
print('Permissions denied');
// Optionally, show a dialog to direct users to settings to manually grant permissions
}
}
}