import 'package:collection/collection.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:stack_trace/stack_trace.dart'; abstract class ReportService { Future init(); bool get isCollectionEnabled; Future setCollectionEnabled(bool enabled); Future log(String message); Future setCustomKey(String key, Object value); Future setCustomKeys(Map map); Future recordError(dynamic exception, StackTrace? stack); Future recordFlutterError(FlutterErrorDetails flutterErrorDetails); } class CrashlyticsReportService extends ReportService { FirebaseCrashlytics get _instance => FirebaseCrashlytics.instance; @override Future init() => Firebase.initializeApp(); @override bool get isCollectionEnabled => _instance.isCrashlyticsCollectionEnabled; @override Future setCollectionEnabled(bool enabled) async { debugPrint('${enabled ? 'enable' : 'disable'} Firebase & Crashlytics collection'); await Firebase.app().setAutomaticDataCollectionEnabled(enabled); await _instance.setCrashlyticsCollectionEnabled(enabled); } @override Future log(String message) => _instance.log(message); @override Future setCustomKey(String key, Object value) => _instance.setCustomKey(key, value); @override Future setCustomKeys(Map map) { return Future.forEach>(map.entries, (kv) => _instance.setCustomKey(kv.key, kv.value)); } @override Future recordError(dynamic exception, StackTrace? stack) { if (exception is PlatformException && stack != null) { // simply creating a trace with `Trace.current(1)` or creating a `Trace` from modified frames // does not yield a stack trace that Crashlytics can segment, // so we reconstruct a string stack trace instead stack = StackTrace.fromString(Trace.from(stack) .frames .skip(2) .toList() .mapIndexed( (i, f) => '#${(i++).toString().padRight(8)}${f.member} (${f.uri}:${f.line}:${f.column})', ) .join('\n')); } return _instance.recordError(exception, stack); } @override Future recordFlutterError(FlutterErrorDetails flutterErrorDetails) { return _instance.recordFlutterError(flutterErrorDetails); } }