Flutter应用集成Firebase Crashlytics如何合规苹果Required Reason API?
适配苹果7D9E.1规范:Flutter + Firebase Crashlytics 实现用户可控的崩溃报告提交流程
Firebase Crashlytics本身不提供访问已收集系统数据(如剩余RAM、磁盘空间)的接口,要满足苹果的要求,得换个思路:自己主动收集所需系统数据,和Crashlytics的错误信息结合,先展示给用户确认,再完成上报。具体实现步骤如下:
1. 手动收集系统状态数据
借助Flutter第三方包获取苹果要求的磁盘空间、剩余RAM等信息:
- 磁盘空间:使用
path_provider获取应用沙盒所在磁盘的可用空间 - 剩余RAM:使用
flutter_memory_info获取设备当前剩余内存
示例代码:
import 'dart:io'; import 'package:path_provider/path_provider.dart'; import 'package:flutter_memory_info/flutter_memory_info.dart'; // 获取剩余磁盘空间(单位:MB) Future<double> getFreeDiskSpace() async { final directory = await getApplicationDocumentsDirectory(); final stat = await directory.stat(); return stat.size / (1024 * 1024); } // 获取剩余RAM(单位:MB) Future<double> getFreeRAM() async { final memoryInfo = await FlutterMemoryInfo().getMemoryInfo(); return memoryInfo.freePhysicalMemory / (1024 * 1024); }
2. 禁用Crashlytics自动上报,改为手动触发
在应用启动时关闭Crashlytics的自动收集,避免未经过用户确认就发送数据:
void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // 禁用自动上报,后续由用户操作触发 await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(false); // 监听Flutter未捕获异常 FlutterError.onError = (details) { _handleUncaughtError(details); }; // 监听Isolate未捕获异常 PlatformDispatcher.instance.onError = (error, stack) { _handleUncaughtError(FlutterErrorDetails( exception: error, stack: stack, )); return true; }; runApp(const MyApp()); }
3. 构建用户确认UI,展示待提交数据
当捕获到异常时,先收集系统数据,然后弹出对话框展示所有待提交内容(错误详情、栈追踪、磁盘空间、剩余RAM等),让用户选择是否提交:
Future<void> _handleUncaughtError(FlutterErrorDetails details) async { // 收集系统数据 final freeDisk = await getFreeDiskSpace(); final freeRAM = await getFreeRAM(); // 构建展示内容 final reportContent = ''' 错误信息: ${details.exception.toString()} 栈追踪: ${details.stack.toString()} 剩余磁盘空间: ${freeDisk.toStringAsFixed(2)} MB 剩余RAM: ${freeRAM.toStringAsFixed(2)} MB '''; // 弹出用户确认对话框 final context = navigatorKey.currentContext; if (context != null) { final shouldSubmit = await showDialog<bool>( context: context, builder: (context) => AlertDialog( title: const Text('发现崩溃'), content: SingleChildScrollView( child: Text(reportContent), ), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: const Text('取消'), ), TextButton( onPressed: () => Navigator.pop(context, true), child: const Text('提交报告'), ), ], ), ); // 根据用户选择处理 if (shouldSubmit == true) { await _submitCrashReport(details, freeDisk, freeRAM); } } }
4. 用户确认后,合并数据提交到Crashlytics
用户确认提交后,先将自定义的系统数据添加为Crashlytics的自定义属性,再上报错误:
Future<void> _submitCrashReport(FlutterErrorDetails details, double freeDisk, double freeRAM) async { // 临时启用收集 await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true); // 添加自定义系统数据属性 await FirebaseCrashlytics.instance.setCustomKey('free_disk_space_mb', freeDisk); await FirebaseCrashlytics.instance.setCustomKey('free_ram_mb', freeRAM); // 上报错误 if (details.fatal) { await FirebaseCrashlytics.instance.recordFlutterFatalError(details); } else { await FirebaseCrashlytics.instance.recordError( details.exception, details.stack, fatal: details.fatal, ); } // 上报完成后,恢复禁用状态(可选,根据需求调整) await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(false); }
关键注意事项
- 确保所有系统数据的收集和发送都在用户确认后执行,完全符合苹果7D9E.1规范的要求
- 自定义属性命名要清晰,方便后续在Firebase控制台筛选和查看相关数据
- 测试离线场景:如果用户确认提交时设备无网络,Crashlytics会自动缓存报告,待网络恢复后发送,无需额外处理
内容的提问来源于stack exchange,提问作者Peter Koltai
相关产品推荐
相关产品推荐

