Xcode模拟器中Flutter应用相机权限自动被拒问题求助
Flutter iOS端二维码扫描问题排查与解决
问题描述
我是Flutter新手,用Android Studio开发了宾客签到二维码扫描应用,Android端运行正常,但iOS端遇到两个问题:
- 点击「开始扫描」时,相机权限自动被拒,尝试通过Settings.bundle让应用显示在模拟器设置中,仍无法授予权限;
- 移除permission_handler后,点击扫描会打开带方框的空白页面,抛出
PlatformException错误,提示未注册平台视图类型<net.touchcapture.qr.flutterqr/qrview>。
相关代码与配置
main.dart
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'guest_list.dart'; void main() { runApp( ChangeNotifierProvider( create: (context) => GuestListProvider(), child: MyApp(), ), ); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Guest Check-In App', theme: ThemeData( primarySwatch: Colors.blue, ), home: StartPage(), ); } } class StartPage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ Positioned.fill( child: Image.asset( 'assets/logo.png', fit: BoxFit.cover, ), ), Center( child: ElevatedButton( onPressed: () { Navigator.push( context, MaterialPageRoute(builder: (context) => HomePage()), ); }, child: Text('Enter'), ), ), ], ), ); } }
guest_list.dart
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:qr_code_scanner/qr_code_scanner.dart'; import 'package:permission_handler/permission_handler.dart'; class GuestListProvider extends ChangeNotifier { final List<String> _guestList = [ 'John Doe', 'Jane Smith', 'Alice Johnson', ]; final Set<String> _checkedInGuests = Set(); List<String> get guestList => _guestList; bool isCheckedIn(String guest) { return _checkedInGuests.contains(guest); } void checkInGuest(String guest) { _checkedInGuests.add(guest); notifyListeners(); } } class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Guest Check-In'), ), body: Stack( children: [ Positioned.fill( child: Container( decoration: BoxDecoration( image: DecorationImage( image: AssetImage('assets/flowery_background.png'), fit: BoxFit.cover, ), color: Colors.white30.withOpacity(0.3), ), ), ), Column( children: [ Expanded( child: GuestListView(), ), Padding( padding: const EdgeInsets.all(8.0), child: ElevatedButton( onPressed: () async { PermissionStatus status = await _getCameraPermission(); if (status.isGranted) { Navigator.push( context, MaterialPageRoute(builder: (context) => QRViewExample()), ); } else if (status.isPermanentlyDenied) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Camera Permission Denied'), content: Text('Camera permission is required to scan QR codes. Please enable it in the settings.'), actions: [ TextButton( onPressed: () { openAppSettings(); Navigator.of(context).pop(); }, child: Text('Open Settings'), ), TextButton( onPressed: () { Navigator.of(context).pop(); }, child: Text('Cancel'), ), ], ), ); } else { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Camera Permission Denied'), content: Text('Camera permission is required to scan QR codes.'), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, child: Text('OK'), ), ], ), ); } }, child: Text('Start Scanning'), ), ), ], ), ], ), ); } Future<PermissionStatus> _getCameraPermission() async { var status = await Permission.camera.status; if (!status.isGranted) { final result = await Permission.camera.request(); return result; } else { return status; } } } class GuestListView extends StatelessWidget { @override Widget build(BuildContext context) { return Consumer<GuestListProvider>( builder: (context, guestListProvider, child) { return ListView.builder( itemCount: guestListProvider.guestList.length, itemBuilder: (context, index) { final guest = guestListProvider.guestList[index]; return ListTile( title: Text(guest), trailing: guestListProvider.isCheckedIn(guest) ? Icon(Icons.check, color: Colors.green) : null, ); }, ); }, ); } } class QRViewExample extends StatefulWidget { @override State<StatefulWidget> createState() => _QRViewExampleState(); } class _QRViewExampleState extends State<QRViewExample> { final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); QRViewController? controller; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Scan QR Code')), body: QRView( key: qrKey, onQRViewCreated: _onQRViewCreated, overlay: QrScannerOverlayShape( borderColor: Colors.red, borderRadius: 10, borderLength: 30, borderWidth: 10, cutOutSize: 300, ), ), ); } void _onQRViewCreated(QRViewController controller) { setState(() { this.controller = controller; }); controller.scannedDataStream.listen((scanData) { final guestName = scanData.code; final guestListProvider = Provider.of<GuestListProvider>(context, listen: false); if (guestName != null) { if (guestListProvider.guestList.contains(guestName)) { if (guestListProvider.isCheckedIn(guestName)) { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Already Checked In'), content: Text('$guestName has already been checked in.'), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, child: Text('OK'), ), ], ), ); } else { guestListProvider.checkInGuest(guestName); showDialog( context: context, builder: (context) => AlertDialog( title: Text('Guest Checked In'), content: Text('$guestName has been checked in.'), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); Navigator.of(context).pop(); }, child: Text('OK'), ), ], ), ); } } else { showDialog( context: context, builder: (context) => AlertDialog( title: Text('Guest Not Found'), content: Text('$guestName is not on the guest list.'), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); Navigator.of(context).pop(); }, child: Text('OK'), ), ], ), ); } } }); } @override void dispose() { controller?.dispose(); super.dispose(); } }
错误信息
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: PlatformException(unregistered_view_type, A UIKitView widget is trying to create a PlatformView with an unregistered type: < net.touchcapture.qr.flutterqr/qrview >, If you are the author of the PlatformView, make sure registerViewFactory is invoked. See: https://docs.flutter.dev/development/platform-integration/platform-views#on-the-platform-side-1 for more details. If you are not the author of the PlatformView, make sure to call GeneratedPluginRegistrant.register., null) #0 StandardMethodCodec.decodeEnvelope (package:flutter/src/services/message_codecs.dart:648:7) #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:334:18) <asynchronous suspension> #2 PlatformViewsService.initUiKitView (package:flutter/src/services/platform_views.dart:243:5) <asynchronous suspension> #3 _DarwinViewState._createNewUiKitView (package:flutter/src/widgets/platform_view.dart:918:36) <asynchronous suspension>
AppDelegate.swift
import UIKit import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }
Info.plist配置
<key>NSCameraUsageDescription</key> <string>This app requires camera access to scan QR codes.</string> <key>NSMicrophoneUsageDescription</key> <string>This app requires microphone access for scanning QR codes.</string>
解决方案
一、相机权限自动拒绝问题处理
- 使用真机测试:iOS模拟器不支持真实相机硬件,权限请求会直接返回拒绝,必须使用iOS物理设备测试相机扫描功能。
- 完善Settings.bundle配置:若要让应用在系统设置中显示相机权限开关,需在
Settings.bundle/Root.plist中添加如下配置项:<array> <!-- 其他配置项 --> <dict> <key>Type</key> <string>PSToggleSwitchSpecifier</string> <key>Title</key> <string>相机权限</string> <key>Key</key> <string>camera_permission</string> <key>DefaultValue</key> <true/> <key>FooterText</key> <string>允许应用访问相机以扫描二维码</string> </dict> </array> - 确认permission_handler配置:在
ios/Podfile中确保iOS版本≥13.0,执行pod install更新依赖:platform :ios, '13.0'
二、平台视图未注册问题处理
- 清理并重新安装依赖:
- 执行
flutter clean清除缓存 - 删除
ios/Podfile.lock文件 - 进入iOS目录执行
pod deintegrate && pod install
- 执行
- 启用平台视图预览:在
ios/Runner/Info.plist中添加以下配置,允许Flutter加载iOS平台视图:<key>io.flutter.embedded_views_preview</key> <true/> - 验证插件版本兼容性:检查
pubspec.yaml中qr_code_scanner的版本,建议使用最新稳定版,避免与当前Flutter版本不兼容。 - 确认插件注册:确保
AppDelegate.swift中已调用GeneratedPluginRegistrant.register(with: self),该代码会自动注册所有Flutter插件的平台视图。
内容的提问来源于stack exchange,提问作者smurfymo
相关产品推荐
相关产品推荐

