Flutter应用调用相机功能后崩溃重启至启动页问题排查
从你提供的日志来看,崩溃的根源并不是相机或裁剪插件本身,而是SplashScreen页面的didChangeDependencies方法中出现了两个空指针错误,相机拍照后App回到前台触发了这些错误。下面是具体的分析和修复步骤:
1. 修复contains调用在null对象上的错误
日志第一条错误显示:
Unhandled Exception: NoSuchMethodError: The method 'contains' was called on null.
Receiver: null
Tried calling: contains("access_token")
#1 _SplashScreenState.didChangeDependencies (package:demo/screens/common/splash_screen.dart:58:28)
这说明你在SplashScreen的第58行,调用了一个未初始化的null变量的contains方法来检查"access_token"。
修复方式:
在调用contains前先判空,或者使用空安全操作符:
// 原代码(错误) if (yourTokenString.contains("access_token")) { ... } // 修复后方案一:先判空 if (yourTokenString != null && yourTokenString.contains("access_token")) { ... } // 修复后方案二:使用空安全操作符(Dart 2.12+) if (yourTokenString?.contains("access_token") ?? false) { ... }
2. 修复Navigator调用时context无效的问题
日志第二条错误显示:
Unhandled Exception: NoSuchMethodError: The method 'findAncestorStateOfType' was called on null.
Receiver: null
Tried calling: findAncestorStateOfType()
#2 _SplashScreenState.didChangeDependencies.(package:demo/screens/common/splash_screen.dart:54:23)
这是因为你在didChangeDependencies里的延迟Future中调用Navigator.of(context)时,当前SplashScreen Widget已经被dispose或者不再挂载在Widget树中(相机拍照会把App推到后台,回来时可能触发页面重建或销毁)。
修复方式:
在调用Navigator前先检查Widget是否还处于mounted状态:
// 原代码(错误) Future.delayed(Duration(seconds: 2), () { Navigator.of(context).pushReplacementNamed("/home"); }); // 修复后 Future.delayed(Duration(seconds: 2), () { if (!mounted) return; // 先判断Widget是否还挂载 Navigator.of(context).pushReplacementNamed("/home"); });
3. 优化相机拍照与裁剪的流程
你的getImage方法存在逻辑顺序问题:_pickInProgress的判断放在了拍照和裁剪之后,而且没有用try-finally确保状态重置,这可能导致异常后状态卡住。
修正后的getImage方法:
Future getImage() async { // 先判断是否正在处理,防止重复调用 if (_pickInProgress) { return; } _pickInProgress = true; try { var _image = await ImagePicker.pickImage(source: ImageSource.camera); // 用户取消拍照时直接返回 if (_image == null) { return; } croppedFile = await ImageCropper.cropImage( sourcePath: _image.path, aspectRatioPresets: [ CropAspectRatioPreset.square, CropAspectRatioPreset.ratio3x2, CropAspectRatioPreset.original, CropAspectRatioPreset.ratio4x3, CropAspectRatioPreset.ratio16x9 ], androidUiSettings: AndroidUiSettings( toolbarTitle: 'Cropper', toolbarColor: Colors.deepOrange, toolbarWidgetColor: Colors.white, initAspectRatio: CropAspectRatioPreset.square, lockAspectRatio: false), iosUiSettings: IOSUiSettings( minimumAspectRatio: 1.0, ) ); if (croppedFile != null) { setState(() { _newAvatar = croppedFile; }); } } catch (e) { // 捕获异常,防止崩溃并打印日志 print("拍照/裁剪过程出错: $e"); } finally { // 无论成功或失败,都重置状态 _pickInProgress = false; } }
4. 额外检查:Android权限配置
确保你已经在AndroidManifest.xml中添加了必要的权限,并且在运行时请求了这些权限:
<!-- 在AndroidManifest.xml的<manifest>标签下添加 --> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- Android 13+ --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
可以使用permission_handler插件来便捷地请求运行时权限,避免因权限不足导致的隐性异常。
内容的提问来源于stack exchange,提问作者princeoo7

