You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 21:05:28