From a11b48db7c66e5d825ef6cf0b0c4186a67eec7c1 Mon Sep 17 00:00:00 2001 From: Mohamed Kamal Date: Sun, 6 Jul 2025 16:02:52 +0300 Subject: [PATCH 01/10] feat: add InstabugWidget for error handling in Flutter apps - Introduced `InstabugWidget` to wrap the root of the application, providing custom error handling for both Flutter and platform errors. - Implemented error reporting to Instabug with options for user-defined error handlers. - Ensured that any exceptions in custom handlers are logged without disrupting Instabug's reporting functionality. --- lib/src/utils/instabug_widget.dart | 125 +++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 lib/src/utils/instabug_widget.dart diff --git a/lib/src/utils/instabug_widget.dart b/lib/src/utils/instabug_widget.dart new file mode 100644 index 000000000..1ca4f2652 --- /dev/null +++ b/lib/src/utils/instabug_widget.dart @@ -0,0 +1,125 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:instabug_flutter/src/modules/crash_reporting.dart'; +import 'package:instabug_flutter/src/utils/instabug_logger.dart'; + +class InstabugWidget extends StatefulWidget { + final Widget child; + + /// Custom handler for Flutter errors. + /// + /// This callback is called when a Flutter error occurs. It receives a + /// [FlutterErrorDetails] object containing information about the error. + /// + /// Example: + /// ```dart + /// InstabugWidget( + /// flutterErrorHandler: (details) { + /// print('Flutter error: ${details.exception}'); + /// // Custom error handling logic + /// }, + /// child: MyApp(), + /// ) + /// ``` + /// + /// Note: If this handler throws an error, it will be caught and logged + /// to prevent it from interfering with Instabug's error reporting. + final Function(FlutterErrorDetails)? flutterErrorHandler; + + /// Custom handler for platform errors. + /// + /// This callback is called when a platform error occurs. It receives the + /// error object and stack trace. + /// + /// Example: + /// ```dart + /// InstabugWidget( + /// platformErrorHandler: (error, stack) { + /// print('Platform error: $error'); + /// // Custom error handling logic + /// }, + /// child: MyApp(), + /// ) + /// ``` + /// + /// Note: If this handler throws an error, it will be caught and logged + /// to prevent it from interfering with Instabug's error reporting. + final Function(Object, StackTrace)? platformErrorHandler; + + /// This widget is used to wrap the root of your application. It will automatically + /// configure both FlutterError.onError and PlatformDispatcher.instance.onError handlers to report errors to Instabug. + /// + /// Example: + /// ```dart + /// MaterialApp( + /// home: InstabugWidget( + /// child: MyApp(), + /// ), + /// ) + /// ``` + /// + /// Note: Custom error handlers should be provided to handle errors before they are reported to Instabug. + const InstabugWidget({ + Key? key, + required this.child, + this.flutterErrorHandler, + this.platformErrorHandler, + }) : super(key: key); + + @override + State createState() => _InstabugWidgetState(); +} + +class _InstabugWidgetState extends State { + @override + void initState() { + super.initState(); + _setupErrorHandlers(); + } + + void _setupErrorHandlers() { + FlutterError.onError = (FlutterErrorDetails details) { + // Call user's custom handler if provided + if (widget.flutterErrorHandler != null) { + try { + widget.flutterErrorHandler!(details); + } catch (e) { + InstabugLogger.I.e( + 'Custom Flutter error handler failed: $e', + tag: 'InstabugWidget', + ); + } + } + + CrashReporting.reportCrash( + details.exception, + details.stack ?? StackTrace.current, + ); + + FlutterError.presentError(details); + }; + + PlatformDispatcher.instance.onError = (Object error, StackTrace stack) { + // Call user's custom handler if provided + if (widget.platformErrorHandler != null) { + try { + widget.platformErrorHandler!(error, stack); + } catch (e) { + InstabugLogger.I.e( + 'Custom platform error handler failed: $e', + tag: 'InstabugWidget', + ); + } + } + + CrashReporting.reportCrash(error, stack); + + return true; + }; + } + + @override + Widget build(BuildContext context) { + return widget.child; + } +} From 0dc978d5c722c1b882fd05f36c8cddd4cc239880 Mon Sep 17 00:00:00 2001 From: Mohamed Kamal Date: Mon, 7 Jul 2025 17:08:31 +0300 Subject: [PATCH 02/10] feat: add more options to instabugWidget - Added `nonFatalFlutterErrors` and `shouldExitOnFlutterError` properties to `InstabugWidget` for improved error management. - Updated error reporting logic to handle non-fatal crashes and conditionally exit the app on Flutter errors. --- lib/src/utils/instabug_widget.dart | 57 +++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/lib/src/utils/instabug_widget.dart b/lib/src/utils/instabug_widget.dart index 1ca4f2652..5f4655138 100644 --- a/lib/src/utils/instabug_widget.dart +++ b/lib/src/utils/instabug_widget.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:instabug_flutter/src/modules/crash_reporting.dart'; @@ -7,10 +9,10 @@ class InstabugWidget extends StatefulWidget { final Widget child; /// Custom handler for Flutter errors. - /// + /// /// This callback is called when a Flutter error occurs. It receives a /// [FlutterErrorDetails] object containing information about the error. - /// + /// /// Example: /// ```dart /// InstabugWidget( @@ -21,16 +23,16 @@ class InstabugWidget extends StatefulWidget { /// child: MyApp(), /// ) /// ``` - /// + /// /// Note: If this handler throws an error, it will be caught and logged /// to prevent it from interfering with Instabug's error reporting. final Function(FlutterErrorDetails)? flutterErrorHandler; - + /// Custom handler for platform errors. - /// + /// /// This callback is called when a platform error occurs. It receives the /// error object and stack trace. - /// + /// /// Example: /// ```dart /// InstabugWidget( @@ -41,29 +43,41 @@ class InstabugWidget extends StatefulWidget { /// child: MyApp(), /// ) /// ``` - /// + /// /// Note: If this handler throws an error, it will be caught and logged /// to prevent it from interfering with Instabug's error reporting. final Function(Object, StackTrace)? platformErrorHandler; + /// Whether to handle Flutter errors. + /// + /// If true, the Flutter error will be reported as a non-fatal crash, instead of a fatal crash. + final bool nonFatalFlutterErrors; + + /// Whether to exit the app on Flutter error. + /// + /// If true, the app will exit when a Flutter error occurs. + final bool shouldExitOnFlutterError; + /// This widget is used to wrap the root of your application. It will automatically /// configure both FlutterError.onError and PlatformDispatcher.instance.onError handlers to report errors to Instabug. - /// + /// /// Example: - /// ```dart + /// ```dart /// MaterialApp( /// home: InstabugWidget( /// child: MyApp(), /// ), /// ) /// ``` - /// - /// Note: Custom error handlers should be provided to handle errors before they are reported to Instabug. + /// + /// Note: Custom error handlers are called before the error is reported to Instabug. const InstabugWidget({ Key? key, required this.child, this.flutterErrorHandler, this.platformErrorHandler, + this.nonFatalFlutterErrors = false, + this.shouldExitOnFlutterError = false, }) : super(key: key); @override @@ -91,14 +105,25 @@ class _InstabugWidgetState extends State { } } - CrashReporting.reportCrash( - details.exception, - details.stack ?? StackTrace.current, - ); + if (widget.nonFatalFlutterErrors) { + CrashReporting.reportHandledCrash( + details.exception, + details.stack ?? StackTrace.current, + ); + } else { + CrashReporting.reportCrash( + details.exception, + details.stack ?? StackTrace.current, + ); + } FlutterError.presentError(details); - }; + if (widget.shouldExitOnFlutterError) { + exit(1); + } + }; + PlatformDispatcher.instance.onError = (Object error, StackTrace stack) { // Call user's custom handler if provided if (widget.platformErrorHandler != null) { From 62afd05833489b9c6e75633cdfb07e25956d81fa Mon Sep 17 00:00:00 2001 From: Mohamed Kamal Date: Mon, 7 Jul 2025 17:11:43 +0300 Subject: [PATCH 03/10] feat: enhance non-fatal exceptions control inside InstabugWidget - Added `nonFatalExceptionLevel` property to `InstabugWidget` for better granularity in error reporting. --- lib/src/utils/instabug_widget.dart | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/src/utils/instabug_widget.dart b/lib/src/utils/instabug_widget.dart index 5f4655138..0b3493e6e 100644 --- a/lib/src/utils/instabug_widget.dart +++ b/lib/src/utils/instabug_widget.dart @@ -53,6 +53,13 @@ class InstabugWidget extends StatefulWidget { /// If true, the Flutter error will be reported as a non-fatal crash, instead of a fatal crash. final bool nonFatalFlutterErrors; + /// The level of the non-fatal exception. + /// + /// This is used to determine the level of the non-fatal exception. + /// + /// Note: This has no effect if [nonFatalFlutterErrors] is false. + final NonFatalExceptionLevel nonFatalExceptionLevel; + /// Whether to exit the app on Flutter error. /// /// If true, the app will exit when a Flutter error occurs. @@ -77,6 +84,7 @@ class InstabugWidget extends StatefulWidget { this.flutterErrorHandler, this.platformErrorHandler, this.nonFatalFlutterErrors = false, + this.nonFatalExceptionLevel = NonFatalExceptionLevel.error, this.shouldExitOnFlutterError = false, }) : super(key: key); @@ -109,6 +117,7 @@ class _InstabugWidgetState extends State { CrashReporting.reportHandledCrash( details.exception, details.stack ?? StackTrace.current, + level: widget.nonFatalExceptionLevel, ); } else { CrashReporting.reportCrash( @@ -123,7 +132,7 @@ class _InstabugWidgetState extends State { exit(1); } }; - + PlatformDispatcher.instance.onError = (Object error, StackTrace stack) { // Call user's custom handler if provided if (widget.platformErrorHandler != null) { From c2bffd667bea8074b5a2191e5d1f4de0c78b07a7 Mon Sep 17 00:00:00 2001 From: Mohamed Kamal Date: Mon, 7 Jul 2025 19:32:13 +0300 Subject: [PATCH 04/10] fix: remove shouldExitOnFlutterError until further tested --- lib/src/utils/instabug_widget.dart | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/lib/src/utils/instabug_widget.dart b/lib/src/utils/instabug_widget.dart index 0b3493e6e..126f5ed14 100644 --- a/lib/src/utils/instabug_widget.dart +++ b/lib/src/utils/instabug_widget.dart @@ -60,11 +60,6 @@ class InstabugWidget extends StatefulWidget { /// Note: This has no effect if [nonFatalFlutterErrors] is false. final NonFatalExceptionLevel nonFatalExceptionLevel; - /// Whether to exit the app on Flutter error. - /// - /// If true, the app will exit when a Flutter error occurs. - final bool shouldExitOnFlutterError; - /// This widget is used to wrap the root of your application. It will automatically /// configure both FlutterError.onError and PlatformDispatcher.instance.onError handlers to report errors to Instabug. /// @@ -85,7 +80,6 @@ class InstabugWidget extends StatefulWidget { this.platformErrorHandler, this.nonFatalFlutterErrors = false, this.nonFatalExceptionLevel = NonFatalExceptionLevel.error, - this.shouldExitOnFlutterError = false, }) : super(key: key); @override @@ -127,10 +121,6 @@ class _InstabugWidgetState extends State { } FlutterError.presentError(details); - - if (widget.shouldExitOnFlutterError) { - exit(1); - } }; PlatformDispatcher.instance.onError = (Object error, StackTrace stack) { From 26e2e9c751f150dd859475aa13bb628e4022c212 Mon Sep 17 00:00:00 2001 From: Mohamed Kamal Date: Mon, 7 Jul 2025 19:33:31 +0300 Subject: [PATCH 05/10] fix: send handled crashes now correctly in non-fatal crashes section --- example/lib/src/components/non_fatal_crashes_content.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/example/lib/src/components/non_fatal_crashes_content.dart b/example/lib/src/components/non_fatal_crashes_content.dart index c3d331187..3f4b06da7 100644 --- a/example/lib/src/components/non_fatal_crashes_content.dart +++ b/example/lib/src/components/non_fatal_crashes_content.dart @@ -21,6 +21,10 @@ class _NonFatalCrashesContentState extends State { log('throwHandledException: Crash report for ${err.runtimeType} is Sent!', name: 'NonFatalCrashesWidget'); } + CrashReporting.reportHandledCrash( + err, + StackTrace.current, + ); } } From 9289de17950b172b1751d43c10cbde84e32e8f63 Mon Sep 17 00:00:00 2001 From: Mohamed Kamal Date: Mon, 7 Jul 2025 19:33:57 +0300 Subject: [PATCH 06/10] chore: use new InstabugWidget inside example app --- example/lib/main.dart | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 91b0a67e7..59cbccdfc 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -5,6 +5,7 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:instabug_flutter/instabug_flutter.dart'; +import 'package:instabug_flutter/src/utils/instabug_widget.dart'; import 'package:instabug_flutter_example/src/components/apm_switch.dart'; import 'package:instabug_http_client/instabug_http_client.dart'; import 'package:instabug_flutter_example/src/app_routes.dart'; @@ -43,9 +44,7 @@ part 'src/components/traces_content.dart'; part 'src/components/flows_content.dart'; void main() { - runZonedGuarded( - () { - WidgetsFlutterBinding.ensureInitialized(); + WidgetsFlutterBinding.ensureInitialized(); Instabug.init( token: 'ed6f659591566da19b67857e1b9d40ab', @@ -53,14 +52,11 @@ void main() { debugLogsLevel: LogLevel.verbose, ); - FlutterError.onError = (FlutterErrorDetails details) { - Zone.current.handleUncaughtError(details.exception, details.stack!); - }; - - runApp(const MyApp()); - }, - CrashReporting.reportCrash, + final app = InstabugWidget( + child: const MyApp(), ); + + runApp(app); } class MyApp extends StatelessWidget { From abddbe65bddfffead039bbb74ebf370f35b110ef Mon Sep 17 00:00:00 2001 From: Mohamed Kamal Date: Mon, 7 Jul 2025 19:44:08 +0300 Subject: [PATCH 07/10] chore: updated changelog --- CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86c0b6245..81e14889b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog -## [15.0.2](https://github.com/Instabug/Instabug-Flutter/compare/v14.3.0...15.0.1) (Jul 7, 2025) +## [Unreleased](https://github.com/Instabug/Instabug-Flutter/compare/v14.3.0...dev) + +### Added + +- Added `InstabugWidget`, a wrapper used to wrap the main application to provide out of the box automatic Crash Reporting support. ([#598](https://github.com/Instabug/Instabug-Flutter/pull/598)) + +## [15.0.1](https://github.com/Instabug/Instabug-Flutter/compare/v14.3.0...15.0.1) ### Added From fcc3782efd016e48d946955ac989be15dcd10104 Mon Sep 17 00:00:00 2001 From: Mohamed Kamal Date: Mon, 7 Jul 2025 19:49:51 +0300 Subject: [PATCH 08/10] chore: run dart format --- example/lib/main.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/example/lib/main.dart b/example/lib/main.dart index 59cbccdfc..51d712663 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -46,11 +46,11 @@ part 'src/components/flows_content.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); - Instabug.init( - token: 'ed6f659591566da19b67857e1b9d40ab', - invocationEvents: [InvocationEvent.floatingButton], - debugLogsLevel: LogLevel.verbose, - ); + Instabug.init( + token: 'ed6f659591566da19b67857e1b9d40ab', + invocationEvents: [InvocationEvent.floatingButton], + debugLogsLevel: LogLevel.verbose, + ); final app = InstabugWidget( child: const MyApp(), From 0c87757acf52310b2ea25c7d9e7234c460ed0b83 Mon Sep 17 00:00:00 2001 From: Mohamed Kamal Date: Mon, 7 Jul 2025 19:56:02 +0300 Subject: [PATCH 09/10] chore: remove unused import --- lib/src/utils/instabug_widget.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/src/utils/instabug_widget.dart b/lib/src/utils/instabug_widget.dart index 126f5ed14..e2c7ae271 100644 --- a/lib/src/utils/instabug_widget.dart +++ b/lib/src/utils/instabug_widget.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:instabug_flutter/src/modules/crash_reporting.dart'; From 52108778838c46c80866bc01539bbfb62713f928 Mon Sep 17 00:00:00 2001 From: Mohamed Kamal Date: Tue, 8 Jul 2025 18:54:44 +0300 Subject: [PATCH 10/10] chore: fix version mistype in changelog --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81e14889b..fad1d3f6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,12 @@ # Changelog -## [Unreleased](https://github.com/Instabug/Instabug-Flutter/compare/v14.3.0...dev) +## [Unreleased](https://github.com/Instabug/Instabug-Flutter/compare/v15.0.1...dev) ### Added - Added `InstabugWidget`, a wrapper used to wrap the main application to provide out of the box automatic Crash Reporting support. ([#598](https://github.com/Instabug/Instabug-Flutter/pull/598)) -## [15.0.1](https://github.com/Instabug/Instabug-Flutter/compare/v14.3.0...15.0.1) +## [15.0.2](https://github.com/Instabug/Instabug-Flutter/compare/v14.3.0...15.0.1) ### Added