From 1b36c563213d90b7fd2009ab3f5d2791f9001c69 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 21 Oct 2024 13:26:55 +0200 Subject: [PATCH 01/15] add multiview helper to make the sentry widget, multiview aware. --- flutter/lib/src/sentry_widget.dart | 12 +++++++++++- .../src/utils/multi_view/html_multi_view_helper.dart | 11 +++++++++++ .../src/utils/multi_view/io_multi_view_helper.dart | 10 ++++++++++ .../lib/src/utils/multi_view/multi_view_helper.dart | 8 ++++++++ .../src/utils/multi_view/web_multi_view_helper.dart | 11 +++++++++++ 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 flutter/lib/src/utils/multi_view/html_multi_view_helper.dart create mode 100644 flutter/lib/src/utils/multi_view/io_multi_view_helper.dart create mode 100644 flutter/lib/src/utils/multi_view/multi_view_helper.dart create mode 100644 flutter/lib/src/utils/multi_view/web_multi_view_helper.dart diff --git a/flutter/lib/src/sentry_widget.dart b/flutter/lib/src/sentry_widget.dart index 40709a5465..a97211af25 100644 --- a/flutter/lib/src/sentry_widget.dart +++ b/flutter/lib/src/sentry_widget.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:meta/meta.dart'; import '../sentry_flutter.dart'; +import 'utils/multi_view/multi_view_helper.dart'; /// Key which is used to identify the [SentryWidget] @internal @@ -11,7 +12,13 @@ final sentryWidgetGlobalKey = GlobalKey(debugLabel: 'sentry_widget'); class SentryWidget extends StatefulWidget { final Widget child; - const SentryWidget({super.key, required this.child}); + SentryWidget({ + super.key, + required this.child, + @internal Hub? hub, + }); + + final bool _isMultiViewEnabled = MultiViewHelper().isMultiViewEnabled(); @override _SentryWidgetState createState() => _SentryWidgetState(); @@ -21,6 +28,9 @@ class _SentryWidgetState extends State { @override Widget build(BuildContext context) { Widget content = widget.child; + if (widget._isMultiViewEnabled) { + return content; + } content = SentryScreenshotWidget(child: content); content = SentryUserInteractionWidget(child: content); return Container( diff --git a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart new file mode 100644 index 0000000000..10ef1b9179 --- /dev/null +++ b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart @@ -0,0 +1,11 @@ +import 'dart:js' as js; +import 'multi_view_helper.dart'; + +MultiViewHelper multiViewHelper() => HtmlMultiViewHelper(); + +class HtmlMultiViewHelper implements MultiViewHelper { + @override + bool isMultiViewEnabled() { + return "flutter-view" == js.context['__flutterState'][0].toString(); + } +} diff --git a/flutter/lib/src/utils/multi_view/io_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/io_multi_view_helper.dart new file mode 100644 index 0000000000..6e34b265bf --- /dev/null +++ b/flutter/lib/src/utils/multi_view/io_multi_view_helper.dart @@ -0,0 +1,10 @@ +import 'multi_view_helper.dart'; + +MultiViewHelper multiViewHelper() => IoMultiViewHelper(); + +class IoMultiViewHelper implements MultiViewHelper { + @override + bool isMultiViewEnabled() { + return false; + } +} diff --git a/flutter/lib/src/utils/multi_view/multi_view_helper.dart b/flutter/lib/src/utils/multi_view/multi_view_helper.dart new file mode 100644 index 0000000000..db850d6627 --- /dev/null +++ b/flutter/lib/src/utils/multi_view/multi_view_helper.dart @@ -0,0 +1,8 @@ +import 'io_multi_view_helper.dart' + if (dart.library.html) 'html_multi_view_helper.dart' + if (dart.library.js_interop) 'web_multi_view_helper.dart'; + +abstract class MultiViewHelper { + bool isMultiViewEnabled(); + factory MultiViewHelper() => multiViewHelper(); +} diff --git a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart new file mode 100644 index 0000000000..69c1622ac4 --- /dev/null +++ b/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart @@ -0,0 +1,11 @@ +import 'dart:js' as js; +import 'multi_view_helper.dart'; + +MultiViewHelper multiViewHelper() => WebMultiViewHelper(); + +class WebMultiViewHelper implements MultiViewHelper { + @override + bool isMultiViewEnabled() { + return "flutter-view" == js.context['__flutterState'][0].toString(); + } +} From f6239fb0c6c658de2c9f71b7b9b88f0f4a8b9092 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 21 Oct 2024 14:20:40 +0200 Subject: [PATCH 02/15] switch to implicit view approach --- flutter/lib/src/utils/multi_view/html_multi_view_helper.dart | 4 ++-- flutter/lib/src/utils/multi_view/web_multi_view_helper.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart index 10ef1b9179..8ad630d18d 100644 --- a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart +++ b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart @@ -1,4 +1,4 @@ -import 'dart:js' as js; +import 'dart:ui'; import 'multi_view_helper.dart'; MultiViewHelper multiViewHelper() => HtmlMultiViewHelper(); @@ -6,6 +6,6 @@ MultiViewHelper multiViewHelper() => HtmlMultiViewHelper(); class HtmlMultiViewHelper implements MultiViewHelper { @override bool isMultiViewEnabled() { - return "flutter-view" == js.context['__flutterState'][0].toString(); + return null == PlatformDispatcher.instance.implicitView; } } diff --git a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart index 69c1622ac4..93b64ed597 100644 --- a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart +++ b/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart @@ -1,4 +1,4 @@ -import 'dart:js' as js; +import 'dart:ui'; import 'multi_view_helper.dart'; MultiViewHelper multiViewHelper() => WebMultiViewHelper(); @@ -6,6 +6,6 @@ MultiViewHelper multiViewHelper() => WebMultiViewHelper(); class WebMultiViewHelper implements MultiViewHelper { @override bool isMultiViewEnabled() { - return "flutter-view" == js.context['__flutterState'][0].toString(); + return null == PlatformDispatcher.instance.implicitView; } } From 36a84aa6e6702bf2b4640694a5d984a878e46dcb Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 21 Oct 2024 14:28:04 +0200 Subject: [PATCH 03/15] add changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38f93e41b2..5bc5a981b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Features + +- add multiview helper to make the sentry widget multiview aware for the web platform ([#2366](https://github.com/getsentry/sentry-dart/pull/2366)) + ## 8.10.0-beta.2 ### Fixes From 540d1c022d9cde30c3fd13f44e1d0de6a1aa59d6 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 21 Oct 2024 16:44:45 +0200 Subject: [PATCH 04/15] make it flutter 3.0.0 compatible --- .../lib/src/utils/multi_view/html_multi_view_helper.dart | 6 +++++- flutter/lib/src/utils/multi_view/web_multi_view_helper.dart | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart index 8ad630d18d..73bbe952da 100644 --- a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart +++ b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart @@ -6,6 +6,10 @@ MultiViewHelper multiViewHelper() => HtmlMultiViewHelper(); class HtmlMultiViewHelper implements MultiViewHelper { @override bool isMultiViewEnabled() { - return null == PlatformDispatcher.instance.implicitView; + try { + return null == PlatformDispatcher.instance.implicitView; + } on NoSuchMethodError catch (_) { + return false; + } } } diff --git a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart index 93b64ed597..dd6e626550 100644 --- a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart +++ b/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart @@ -6,6 +6,10 @@ MultiViewHelper multiViewHelper() => WebMultiViewHelper(); class WebMultiViewHelper implements MultiViewHelper { @override bool isMultiViewEnabled() { - return null == PlatformDispatcher.instance.implicitView; + try { + return null == PlatformDispatcher.instance.implicitView; + } on NoSuchMethodError catch (_) { + return false; + } } } From 1dab88e3d75bf20b3e8020265c0ef75f9038fcd7 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 21 Oct 2024 16:55:19 +0200 Subject: [PATCH 05/15] fix exception --- flutter/lib/src/utils/multi_view/html_multi_view_helper.dart | 4 +++- flutter/lib/src/utils/multi_view/web_multi_view_helper.dart | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart index 73bbe952da..f065006814 100644 --- a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart +++ b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart @@ -6,8 +6,10 @@ MultiViewHelper multiViewHelper() => HtmlMultiViewHelper(); class HtmlMultiViewHelper implements MultiViewHelper { @override bool isMultiViewEnabled() { + final dynamic uncheckedImplicitView = + PlatformDispatcher.instance.implicitView; try { - return null == PlatformDispatcher.instance.implicitView; + return null == uncheckedImplicitView; } on NoSuchMethodError catch (_) { return false; } diff --git a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart index dd6e626550..504fd52721 100644 --- a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart +++ b/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart @@ -6,8 +6,10 @@ MultiViewHelper multiViewHelper() => WebMultiViewHelper(); class WebMultiViewHelper implements MultiViewHelper { @override bool isMultiViewEnabled() { + final dynamic uncheckedImplicitView = + PlatformDispatcher.instance.implicitView; try { - return null == PlatformDispatcher.instance.implicitView; + return null == uncheckedImplicitView; } on NoSuchMethodError catch (_) { return false; } From 179091d9c18f5babf8b7920dbaf10cdc75e31818 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 21 Oct 2024 17:20:22 +0200 Subject: [PATCH 06/15] fix exception --- flutter/lib/src/utils/multi_view/html_multi_view_helper.dart | 5 ++--- flutter/lib/src/utils/multi_view/web_multi_view_helper.dart | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart index f065006814..98ea17ffae 100644 --- a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart +++ b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart @@ -6,10 +6,9 @@ MultiViewHelper multiViewHelper() => HtmlMultiViewHelper(); class HtmlMultiViewHelper implements MultiViewHelper { @override bool isMultiViewEnabled() { - final dynamic uncheckedImplicitView = - PlatformDispatcher.instance.implicitView; + final dynamic uncheckedImplicitView = PlatformDispatcher.instance; try { - return null == uncheckedImplicitView; + return null == uncheckedImplicitView.implicitView; } on NoSuchMethodError catch (_) { return false; } diff --git a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart index 504fd52721..8419298f44 100644 --- a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart +++ b/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart @@ -6,10 +6,9 @@ MultiViewHelper multiViewHelper() => WebMultiViewHelper(); class WebMultiViewHelper implements MultiViewHelper { @override bool isMultiViewEnabled() { - final dynamic uncheckedImplicitView = - PlatformDispatcher.instance.implicitView; + final dynamic uncheckedImplicitView = PlatformDispatcher.instance; try { - return null == uncheckedImplicitView; + return null == uncheckedImplicitView.implicitView; } on NoSuchMethodError catch (_) { return false; } From 3e8ab979d687d80217e4df2873bdecd6e4e351e4 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 22 Oct 2024 09:44:33 +0200 Subject: [PATCH 07/15] remove unused platform code --- flutter/lib/src/sentry_widget.dart | 4 ++-- .../utils/multi_view/html_multi_view_helper.dart | 16 ---------------- .../utils/multi_view/io_multi_view_helper.dart | 10 ---------- .../src/utils/multi_view/multi_view_helper.dart | 8 -------- ...i_view_helper.dart => multi_view_helper.dart} | 10 ++++------ 5 files changed, 6 insertions(+), 42 deletions(-) delete mode 100644 flutter/lib/src/utils/multi_view/html_multi_view_helper.dart delete mode 100644 flutter/lib/src/utils/multi_view/io_multi_view_helper.dart delete mode 100644 flutter/lib/src/utils/multi_view/multi_view_helper.dart rename flutter/lib/src/utils/{multi_view/web_multi_view_helper.dart => multi_view_helper.dart} (54%) diff --git a/flutter/lib/src/sentry_widget.dart b/flutter/lib/src/sentry_widget.dart index a97211af25..92a8a0d91e 100644 --- a/flutter/lib/src/sentry_widget.dart +++ b/flutter/lib/src/sentry_widget.dart @@ -1,7 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:meta/meta.dart'; import '../sentry_flutter.dart'; -import 'utils/multi_view/multi_view_helper.dart'; +import 'utils/multi_view_helper.dart'; /// Key which is used to identify the [SentryWidget] @internal @@ -18,7 +18,7 @@ class SentryWidget extends StatefulWidget { @internal Hub? hub, }); - final bool _isMultiViewEnabled = MultiViewHelper().isMultiViewEnabled(); + final bool _isMultiViewEnabled = MultiViewHelper.isMultiViewEnabled(); @override _SentryWidgetState createState() => _SentryWidgetState(); diff --git a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart deleted file mode 100644 index 98ea17ffae..0000000000 --- a/flutter/lib/src/utils/multi_view/html_multi_view_helper.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'dart:ui'; -import 'multi_view_helper.dart'; - -MultiViewHelper multiViewHelper() => HtmlMultiViewHelper(); - -class HtmlMultiViewHelper implements MultiViewHelper { - @override - bool isMultiViewEnabled() { - final dynamic uncheckedImplicitView = PlatformDispatcher.instance; - try { - return null == uncheckedImplicitView.implicitView; - } on NoSuchMethodError catch (_) { - return false; - } - } -} diff --git a/flutter/lib/src/utils/multi_view/io_multi_view_helper.dart b/flutter/lib/src/utils/multi_view/io_multi_view_helper.dart deleted file mode 100644 index 6e34b265bf..0000000000 --- a/flutter/lib/src/utils/multi_view/io_multi_view_helper.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'multi_view_helper.dart'; - -MultiViewHelper multiViewHelper() => IoMultiViewHelper(); - -class IoMultiViewHelper implements MultiViewHelper { - @override - bool isMultiViewEnabled() { - return false; - } -} diff --git a/flutter/lib/src/utils/multi_view/multi_view_helper.dart b/flutter/lib/src/utils/multi_view/multi_view_helper.dart deleted file mode 100644 index db850d6627..0000000000 --- a/flutter/lib/src/utils/multi_view/multi_view_helper.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'io_multi_view_helper.dart' - if (dart.library.html) 'html_multi_view_helper.dart' - if (dart.library.js_interop) 'web_multi_view_helper.dart'; - -abstract class MultiViewHelper { - bool isMultiViewEnabled(); - factory MultiViewHelper() => multiViewHelper(); -} diff --git a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart b/flutter/lib/src/utils/multi_view_helper.dart similarity index 54% rename from flutter/lib/src/utils/multi_view/web_multi_view_helper.dart rename to flutter/lib/src/utils/multi_view_helper.dart index 8419298f44..15512114fb 100644 --- a/flutter/lib/src/utils/multi_view/web_multi_view_helper.dart +++ b/flutter/lib/src/utils/multi_view_helper.dart @@ -1,11 +1,9 @@ import 'dart:ui'; -import 'multi_view_helper.dart'; +import 'package:meta/meta.dart'; -MultiViewHelper multiViewHelper() => WebMultiViewHelper(); - -class WebMultiViewHelper implements MultiViewHelper { - @override - bool isMultiViewEnabled() { +@internal +class MultiViewHelper { + static bool isMultiViewEnabled() { final dynamic uncheckedImplicitView = PlatformDispatcher.instance; try { return null == uncheckedImplicitView.implicitView; From 4f8cb52d8572d0c36531ff2c9d2dc7bf0de6a687 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 22 Oct 2024 09:45:07 +0200 Subject: [PATCH 08/15] automatically disable WidgetsBindingIntegration() for multiview --- flutter/lib/src/sentry_flutter.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index e63366c3f7..f1baeacdfb 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -24,6 +24,7 @@ import 'native/sentry_native_binding.dart'; import 'profiling.dart'; import 'renderer/renderer.dart'; import 'span_frame_metrics_collector.dart'; +import 'utils/multi_view_helper.dart'; import 'version.dart'; import 'view_hierarchy/view_hierarchy_integration.dart'; @@ -163,8 +164,10 @@ mixin SentryFlutter { // Will catch any errors that may occur in the Flutter framework itself. integrations.add(FlutterErrorIntegration()); - // This tracks Flutter application events, such as lifecycle events. - integrations.add(WidgetsBindingIntegration()); + if (!MultiViewHelper.isMultiViewEnabled()) { + // This tracks Flutter application events, such as lifecycle events. + integrations.add(WidgetsBindingIntegration()); + } // The ordering here matters, as we'd like to first start the native integration. // That allow us to send events to the network and then the Flutter integrations. From 218975a23fc696880040e13555065f62373bfec9 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 22 Oct 2024 09:57:50 +0200 Subject: [PATCH 09/15] add debug message, if not available --- flutter/lib/src/sentry_flutter.dart | 5 +++++ flutter/lib/src/sentry_widget.dart | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index f1baeacdfb..acebbd75fe 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -165,6 +165,11 @@ mixin SentryFlutter { integrations.add(FlutterErrorIntegration()); if (!MultiViewHelper.isMultiViewEnabled()) { + // ignore: invalid_use_of_internal_member + Sentry.currentHub.options.logger( + SentryLevel.debug, + '`WidgetsBindingIntegration` is not available in multi-view applications.', + ); // This tracks Flutter application events, such as lifecycle events. integrations.add(WidgetsBindingIntegration()); } diff --git a/flutter/lib/src/sentry_widget.dart b/flutter/lib/src/sentry_widget.dart index 92a8a0d91e..f38ffbc4e0 100644 --- a/flutter/lib/src/sentry_widget.dart +++ b/flutter/lib/src/sentry_widget.dart @@ -29,6 +29,11 @@ class _SentryWidgetState extends State { Widget build(BuildContext context) { Widget content = widget.child; if (widget._isMultiViewEnabled) { + // ignore: invalid_use_of_internal_member + Sentry.currentHub.options.logger( + SentryLevel.debug, + '`SentryScreenshotWidget` and `SentryUserInteractionWidget` is not available in multi-view applications.', + ); return content; } content = SentryScreenshotWidget(child: content); From 0dbce52ee13ed0ff1a81809c205b9b4d4c9c2701 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 22 Oct 2024 12:37:44 +0200 Subject: [PATCH 10/15] fix `WidgetsBindingIntegration` call --- flutter/lib/src/sentry_flutter.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index acebbd75fe..7b34826102 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -164,12 +164,13 @@ mixin SentryFlutter { // Will catch any errors that may occur in the Flutter framework itself. integrations.add(FlutterErrorIntegration()); - if (!MultiViewHelper.isMultiViewEnabled()) { + if (MultiViewHelper.isMultiViewEnabled()) { // ignore: invalid_use_of_internal_member - Sentry.currentHub.options.logger( + options.logger( SentryLevel.debug, '`WidgetsBindingIntegration` is not available in multi-view applications.', ); + } else { // This tracks Flutter application events, such as lifecycle events. integrations.add(WidgetsBindingIntegration()); } From 9e610e42bbe16c95d27b1ffd7e1a3ad49fe7bf75 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 22 Oct 2024 12:54:35 +0200 Subject: [PATCH 11/15] fix, screenshotIntegration call --- flutter/lib/src/sentry_flutter.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index 7b34826102..4fa4a6094e 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -189,7 +189,8 @@ mixin SentryFlutter { } final renderer = options.rendererWrapper.getRenderer(); - if (!platformChecker.isWeb || renderer == FlutterRenderer.canvasKit) { + if (!MultiViewHelper.isMultiViewEnabled() && !platformChecker.isWeb || + renderer == FlutterRenderer.canvasKit) { integrations.add(ScreenshotIntegration()); } From 481b982197d1f33dce1a0c98832d7184d69bee9a Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 28 Oct 2024 08:56:42 +0100 Subject: [PATCH 12/15] Update CHANGELOG.md Co-authored-by: Giancarlo Buenaflor --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b43e101d3..74884dce39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Features -- Add `MultiViewHelper` to make the sentry plugin multiview aware for the web platform and automatically disable `SentryScreenshotWidget`, `SentryUserInteractionWidget` and `WidgetsBindingIntegration` in multi-view applications.([#2366](https://github.com/getsentry/sentry-dart/pull/2366)) +- Make Sentry Flutter multiview aware for the web platform and automatically disable `SentryScreenshotWidget`, `SentryUserInteractionWidget` and `WidgetsBindingIntegration` in multi-view applications.([#2366](https://github.com/getsentry/sentry-dart/pull/2366)) ### Enhancements From 55c47903b6e46d86087f09de016c64c7ffe9a27c Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 28 Oct 2024 09:12:36 +0100 Subject: [PATCH 13/15] Move MultiViewCheck for WidgetsBindingIntegration inside object --- .../integrations/widgets_binding_integration.dart | 10 ++++++++++ flutter/lib/src/sentry_flutter.dart | 12 ++---------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/flutter/lib/src/integrations/widgets_binding_integration.dart b/flutter/lib/src/integrations/widgets_binding_integration.dart index 8ebab7e7af..c020e96180 100644 --- a/flutter/lib/src/integrations/widgets_binding_integration.dart +++ b/flutter/lib/src/integrations/widgets_binding_integration.dart @@ -1,5 +1,6 @@ import 'package:sentry/sentry.dart'; import '../sentry_flutter_options.dart'; +import '../utils/multi_view_helper.dart'; import '../widgets_binding_observer.dart'; /// Integration that captures certain window and device events. @@ -12,6 +13,15 @@ class WidgetsBindingIntegration implements Integration { @override void call(Hub hub, SentryFlutterOptions options) { + if (MultiViewHelper.isMultiViewEnabled()) { + // ignore: invalid_use_of_internal_member + options.logger( + SentryLevel.debug, + '`WidgetsBindingIntegration` is not available in multi-view applications.', + ); + return; + } + _options = options; final observer = SentryWidgetsBindingObserver( hub: hub, diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index 4fa4a6094e..f199f7a0da 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -164,16 +164,8 @@ mixin SentryFlutter { // Will catch any errors that may occur in the Flutter framework itself. integrations.add(FlutterErrorIntegration()); - if (MultiViewHelper.isMultiViewEnabled()) { - // ignore: invalid_use_of_internal_member - options.logger( - SentryLevel.debug, - '`WidgetsBindingIntegration` is not available in multi-view applications.', - ); - } else { - // This tracks Flutter application events, such as lifecycle events. - integrations.add(WidgetsBindingIntegration()); - } + // This tracks Flutter application events, such as lifecycle events. + integrations.add(WidgetsBindingIntegration()); // The ordering here matters, as we'd like to first start the native integration. // That allow us to send events to the network and then the Flutter integrations. From f0e82e4424960bbbb8f0c2002a7aefe30bda042d Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 28 Oct 2024 09:22:44 +0100 Subject: [PATCH 14/15] Move MultiViewCheck for ScreenshotIntegration inside object --- .../lib/src/integrations/screenshot_integration.dart | 10 ++++++++++ flutter/lib/src/sentry_flutter.dart | 3 +-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/flutter/lib/src/integrations/screenshot_integration.dart b/flutter/lib/src/integrations/screenshot_integration.dart index 10cf60228a..feb877d448 100644 --- a/flutter/lib/src/integrations/screenshot_integration.dart +++ b/flutter/lib/src/integrations/screenshot_integration.dart @@ -1,6 +1,7 @@ import 'package:sentry/sentry.dart'; import '../event_processor/screenshot_event_processor.dart'; import '../sentry_flutter_options.dart'; +import '../utils/multi_view_helper.dart'; /// Adds [ScreenshotEventProcessor] to options event processors if /// [SentryFlutterOptions.attachScreenshot] is true @@ -10,6 +11,15 @@ class ScreenshotIntegration implements Integration { @override void call(Hub hub, SentryFlutterOptions options) { + if (MultiViewHelper.isMultiViewEnabled()) { + // ignore: invalid_use_of_internal_member + options.logger( + SentryLevel.debug, + '`ScreenshotIntegration` is not available in multi-view applications.', + ); + return; + } + if (options.attachScreenshot) { _options = options; final screenshotEventProcessor = ScreenshotEventProcessor(options); diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index f199f7a0da..513ec84fc4 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -181,8 +181,7 @@ mixin SentryFlutter { } final renderer = options.rendererWrapper.getRenderer(); - if (!MultiViewHelper.isMultiViewEnabled() && !platformChecker.isWeb || - renderer == FlutterRenderer.canvasKit) { + if (!platformChecker.isWeb || renderer == FlutterRenderer.canvasKit) { integrations.add(ScreenshotIntegration()); } From d52c4fe57e389c6d98d9a9294f233e7ab86801bb Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 28 Oct 2024 16:28:23 +0100 Subject: [PATCH 15/15] move platform dispatcher wrapper in a separate file --- .../integrations/on_error_integration.dart | 44 +---------- flutter/lib/src/sentry_flutter.dart | 2 +- flutter/lib/src/utils/multi_view_helper.dart | 13 ++-- .../utils/platform_dispatcher_wrapper.dart | 73 +++++++++++++++++++ ...ets_binding_on_error_integration_test.dart | 1 + .../on_error_integration_test.dart | 1 + 6 files changed, 84 insertions(+), 50 deletions(-) create mode 100644 flutter/lib/src/utils/platform_dispatcher_wrapper.dart diff --git a/flutter/lib/src/integrations/on_error_integration.dart b/flutter/lib/src/integrations/on_error_integration.dart index 365a3067ab..fad98e6d08 100644 --- a/flutter/lib/src/integrations/on_error_integration.dart +++ b/flutter/lib/src/integrations/on_error_integration.dart @@ -7,6 +7,8 @@ import '../sentry_flutter_options.dart'; // ignore: implementation_imports import 'package:sentry/src/utils/stacktrace_utils.dart'; +import '../utils/platform_dispatcher_wrapper.dart'; + typedef ErrorCallback = bool Function(Object exception, StackTrace stackTrace); /// Integration which captures `PlatformDispatcher.onError` @@ -109,45 +111,3 @@ class OnErrorIntegration implements Integration { } } } - -/// This class wraps the `this as dynamic` hack in a type-safe manner. -/// It helps to introduce code, which uses newer features from Flutter -/// without breaking Sentry on older versions of Flutter. -// Should not become part of public API. -@visibleForTesting -class PlatformDispatcherWrapper { - PlatformDispatcherWrapper(this._dispatcher); - - final PlatformDispatcher? _dispatcher; - - /// Should not be accessed if [isOnErrorSupported] == false - ErrorCallback? get onError => - (_dispatcher as dynamic)?.onError as ErrorCallback?; - - /// Should not be accessed if [isOnErrorSupported] == false - set onError(ErrorCallback? callback) { - (_dispatcher as dynamic)?.onError = callback; - } - - bool isOnErrorSupported(SentryFlutterOptions options) { - try { - onError; - } on NoSuchMethodError { - // This error is expected on pre 3.1 Flutter version - return false; - } catch (exception, stacktrace) { - // This error is neither expected on pre 3.1 nor on >= 3.1 Flutter versions - options.logger( - SentryLevel.debug, - 'An unexpected exception was thrown, please create an issue at https://github.com/getsentry/sentry-dart/issues', - exception: exception, - stackTrace: stacktrace, - ); - if (options.automatedTestMode) { - rethrow; - } - return false; - } - return true; - } -} diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index 513ec84fc4..c863d56968 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -24,7 +24,7 @@ import 'native/sentry_native_binding.dart'; import 'profiling.dart'; import 'renderer/renderer.dart'; import 'span_frame_metrics_collector.dart'; -import 'utils/multi_view_helper.dart'; +import 'utils/platform_dispatcher_wrapper.dart'; import 'version.dart'; import 'view_hierarchy/view_hierarchy_integration.dart'; diff --git a/flutter/lib/src/utils/multi_view_helper.dart b/flutter/lib/src/utils/multi_view_helper.dart index 15512114fb..6b40e74ddf 100644 --- a/flutter/lib/src/utils/multi_view_helper.dart +++ b/flutter/lib/src/utils/multi_view_helper.dart @@ -1,14 +1,13 @@ -import 'dart:ui'; +import 'package:flutter/widgets.dart'; import 'package:meta/meta.dart'; +import 'platform_dispatcher_wrapper.dart'; @internal class MultiViewHelper { + static PlatformDispatcherWrapper wrapper = + PlatformDispatcherWrapper(WidgetsBinding.instance.platformDispatcher); + static bool isMultiViewEnabled() { - final dynamic uncheckedImplicitView = PlatformDispatcher.instance; - try { - return null == uncheckedImplicitView.implicitView; - } on NoSuchMethodError catch (_) { - return false; - } + return wrapper.implicitView == null; } } diff --git a/flutter/lib/src/utils/platform_dispatcher_wrapper.dart b/flutter/lib/src/utils/platform_dispatcher_wrapper.dart new file mode 100644 index 0000000000..14a42ca2b0 --- /dev/null +++ b/flutter/lib/src/utils/platform_dispatcher_wrapper.dart @@ -0,0 +1,73 @@ +import 'dart:ui'; + +import 'package:meta/meta.dart'; +import 'package:sentry/sentry.dart'; +import '../sentry_flutter_options.dart'; + +/// This class wraps the `this as dynamic` hack in a type-safe manner. +/// It helps to introduce code, which uses newer features from Flutter +/// without breaking Sentry on older versions of Flutter. +// Should not become part of public API. +@internal +class PlatformDispatcherWrapper { + PlatformDispatcherWrapper(this._dispatcher); + + final PlatformDispatcher? _dispatcher; + + /// Should not be accessed if [isImplicitViewSupported] == false + FlutterView? get implicitView => + (_dispatcher as dynamic)?.implicitView as FlutterView?; + + bool isImplicitViewSupported(SentryFlutterOptions options) { + try { + implicitView; + } on NoSuchMethodError { + // This error is expected on pre 3.10.0 Flutter version + return false; + } catch (exception, stacktrace) { + // This error is neither expected on pre 3.10.0 nor on >= 3.10.0 Flutter versions + options.logger( + SentryLevel.debug, + 'An unexpected exception was thrown, please create an issue at https://github.com/getsentry/sentry-dart/issues', + exception: exception, + stackTrace: stacktrace, + ); + if (options.automatedTestMode) { + rethrow; + } + return false; + } + return true; + } + + /// Should not be accessed if [isOnErrorSupported] == false + ErrorCallback? get onError => + (_dispatcher as dynamic)?.onError as ErrorCallback?; + + /// Should not be accessed if [isOnErrorSupported] == false + set onError(ErrorCallback? callback) { + (_dispatcher as dynamic)?.onError = callback; + } + + bool isOnErrorSupported(SentryFlutterOptions options) { + try { + onError; + } on NoSuchMethodError { + // This error is expected on pre 3.1 Flutter version + return false; + } catch (exception, stacktrace) { + // This error is neither expected on pre 3.1 nor on >= 3.1 Flutter versions + options.logger( + SentryLevel.debug, + 'An unexpected exception was thrown, please create an issue at https://github.com/getsentry/sentry-dart/issues', + exception: exception, + stackTrace: stacktrace, + ); + if (options.automatedTestMode) { + rethrow; + } + return false; + } + return true; + } +} diff --git a/flutter/test/integrations/not_initialized_widgets_binding_on_error_integration_test.dart b/flutter/test/integrations/not_initialized_widgets_binding_on_error_integration_test.dart index 6df7df0d0f..789c21af79 100644 --- a/flutter/test/integrations/not_initialized_widgets_binding_on_error_integration_test.dart +++ b/flutter/test/integrations/not_initialized_widgets_binding_on_error_integration_test.dart @@ -2,6 +2,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry/sentry.dart'; import 'package:sentry_flutter/src/integrations/on_error_integration.dart'; +import 'package:sentry_flutter/src/utils/platform_dispatcher_wrapper.dart'; import '../mocks.dart'; import '../mocks.mocks.dart'; diff --git a/flutter/test/integrations/on_error_integration_test.dart b/flutter/test/integrations/on_error_integration_test.dart index f367916612..14e7abcb7b 100644 --- a/flutter/test/integrations/on_error_integration_test.dart +++ b/flutter/test/integrations/on_error_integration_test.dart @@ -2,6 +2,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:sentry/sentry.dart'; import 'package:sentry_flutter/src/integrations/on_error_integration.dart'; +import 'package:sentry_flutter/src/utils/platform_dispatcher_wrapper.dart'; import '../mocks.dart'; import '../mocks.mocks.dart';