Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add multiview helper to make the sentry multiview aware. #2366

Draft
wants to merge 17 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- 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

- Cache parsed DSN ([#2365](https://github.com/getsentry/sentry-dart/pull/2365))
Expand Down
44 changes: 2 additions & 42 deletions flutter/lib/src/integrations/on_error_integration.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import 'dart:ui';

Check warning on line 1 in flutter/lib/src/integrations/on_error_integration.dart

View workflow job for this annotation

GitHub Actions / analyze / analyze

Unused import: 'dart:ui'.

Try removing the import directive. See https://dart.dev/diagnostics/unused_import to learn more about this problem.

import 'package:flutter/widgets.dart';

Check warning on line 3 in flutter/lib/src/integrations/on_error_integration.dart

View workflow job for this annotation

GitHub Actions / analyze / analyze

Unused import: 'package:flutter/widgets.dart'.

Try removing the import directive. See https://dart.dev/diagnostics/unused_import to learn more about this problem.
import 'package:sentry/sentry.dart';
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`
Expand Down Expand Up @@ -109,45 +111,3 @@
}
}
}

/// 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;
}
}
10 changes: 10 additions & 0 deletions flutter/lib/src/integrations/screenshot_integration.dart
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -10,6 +11,15 @@ class ScreenshotIntegration implements Integration<SentryFlutterOptions> {

@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);
Expand Down
10 changes: 10 additions & 0 deletions flutter/lib/src/integrations/widgets_binding_integration.dart
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -12,6 +13,15 @@ class WidgetsBindingIntegration implements Integration<SentryFlutterOptions> {

@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,
Expand Down
1 change: 1 addition & 0 deletions flutter/lib/src/sentry_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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/platform_dispatcher_wrapper.dart';
import 'version.dart';
import 'view_hierarchy/view_hierarchy_integration.dart';

Expand Down
17 changes: 16 additions & 1 deletion flutter/lib/src/sentry_widget.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/cupertino.dart';
import 'package:meta/meta.dart';
import '../sentry_flutter.dart';
import 'utils/multi_view_helper.dart';

/// Key which is used to identify the [SentryWidget]
@internal
Expand All @@ -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();
Expand All @@ -21,6 +28,14 @@ class _SentryWidgetState extends State<SentryWidget> {
@override
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);
content = SentryUserInteractionWidget(child: content);
return Container(
Expand Down
13 changes: 13 additions & 0 deletions flutter/lib/src/utils/multi_view_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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() {
return wrapper.implicitView == null;
}
}
73 changes: 73 additions & 0 deletions flutter/lib/src/utils/platform_dispatcher_wrapper.dart
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
1 change: 1 addition & 0 deletions flutter/test/integrations/on_error_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Loading