Skip to content

Commit be2b2de

Browse files
committed
move multiview check to init
1 parent c92c8de commit be2b2de

11 files changed

+105
-131
lines changed

flutter/lib/src/integrations/on_error_integration.dart

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
import 'dart:ui';
22

3-
import 'package:flutter/widgets.dart';
3+
import 'package:meta/meta.dart';
44
import 'package:sentry/sentry.dart';
55
import '../sentry_flutter_options.dart';
66

77
// ignore: implementation_imports
88
import 'package:sentry/src/utils/stacktrace_utils.dart';
99

10-
import '../utils/platform_dispatcher_wrapper.dart';
11-
1210
typedef ErrorCallback = bool Function(Object exception, StackTrace stackTrace);
1311

1412
/// Integration which captures `PlatformDispatcher.onError`
@@ -112,3 +110,66 @@ class OnErrorIntegration implements Integration<SentryFlutterOptions> {
112110
}
113111
}
114112
}
113+
114+
/// This class wraps the `this as dynamic` hack in a type-safe manner.
115+
/// It helps to introduce code, which uses newer features from Flutter
116+
/// without breaking Sentry on older versions of Flutter.
117+
// Should not become part of public API.
118+
@internal
119+
class PlatformDispatcherWrapper {
120+
PlatformDispatcherWrapper(this._dispatcher);
121+
122+
final PlatformDispatcher? _dispatcher;
123+
124+
bool isMultiViewEnabled(SentryFlutterOptions options) {
125+
try {
126+
return ((_dispatcher as dynamic)?.implicitView as FlutterView?) == null;
127+
} on NoSuchMethodError {
128+
// This error is expected on pre 3.10.0 Flutter version
129+
return false;
130+
} catch (exception, stacktrace) {
131+
// This error is neither expected on pre 3.10.0 nor on >= 3.10.0 Flutter versions
132+
options.logger(
133+
SentryLevel.debug,
134+
'An unexpected exception was thrown, please create an issue at https://github.com/getsentry/sentry-dart/issues',
135+
exception: exception,
136+
stackTrace: stacktrace,
137+
);
138+
if (options.automatedTestMode) {
139+
rethrow;
140+
}
141+
return false;
142+
}
143+
}
144+
145+
/// Should not be accessed if [isOnErrorSupported] == false
146+
ErrorCallback? get onError =>
147+
(_dispatcher as dynamic)?.onError as ErrorCallback?;
148+
149+
/// Should not be accessed if [isOnErrorSupported] == false
150+
set onError(ErrorCallback? callback) {
151+
(_dispatcher as dynamic)?.onError = callback;
152+
}
153+
154+
bool isOnErrorSupported(SentryFlutterOptions options) {
155+
try {
156+
onError;
157+
} on NoSuchMethodError {
158+
// This error is expected on pre 3.1 Flutter version
159+
return false;
160+
} catch (exception, stacktrace) {
161+
// This error is neither expected on pre 3.1 nor on >= 3.1 Flutter versions
162+
options.logger(
163+
SentryLevel.debug,
164+
'An unexpected exception was thrown, please create an issue at https://github.com/getsentry/sentry-dart/issues',
165+
exception: exception,
166+
stackTrace: stacktrace,
167+
);
168+
if (options.automatedTestMode) {
169+
rethrow;
170+
}
171+
return false;
172+
}
173+
return true;
174+
}
175+
}

flutter/lib/src/integrations/screenshot_integration.dart

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'package:sentry/sentry.dart';
22
import '../event_processor/screenshot_event_processor.dart';
33
import '../sentry_flutter_options.dart';
4-
import '../utils/multi_view_helper.dart';
54

65
/// Adds [ScreenshotEventProcessor] to options event processors if
76
/// [SentryFlutterOptions.attachScreenshot] is true
@@ -11,15 +10,6 @@ class ScreenshotIntegration implements Integration<SentryFlutterOptions> {
1110

1211
@override
1312
void call(Hub hub, SentryFlutterOptions options) {
14-
if (MultiViewHelper.isMultiViewEnabled()) {
15-
// ignore: invalid_use_of_internal_member
16-
options.logger(
17-
SentryLevel.debug,
18-
'`ScreenshotIntegration` is not available in multi-view applications.',
19-
);
20-
return;
21-
}
22-
2313
if (options.attachScreenshot) {
2414
_options = options;
2515
final screenshotEventProcessor = ScreenshotEventProcessor(options);

flutter/lib/src/integrations/widgets_binding_integration.dart

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'package:sentry/sentry.dart';
22
import '../sentry_flutter_options.dart';
3-
import '../utils/multi_view_helper.dart';
43
import '../widgets_binding_observer.dart';
54

65
/// Integration that captures certain window and device events.
@@ -13,15 +12,6 @@ class WidgetsBindingIntegration implements Integration<SentryFlutterOptions> {
1312

1413
@override
1514
void call(Hub hub, SentryFlutterOptions options) {
16-
if (MultiViewHelper.isMultiViewEnabled()) {
17-
// ignore: invalid_use_of_internal_member
18-
options.logger(
19-
SentryLevel.debug,
20-
'`WidgetsBindingIntegration` is not available in multi-view applications.',
21-
);
22-
return;
23-
}
24-
2515
_options = options;
2616
final observer = SentryWidgetsBindingObserver(
2717
hub: hub,

flutter/lib/src/sentry_flutter.dart

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ import 'native/native_scope_observer.dart';
2525
import 'native/sentry_native_binding.dart';
2626
import 'profiling.dart';
2727
import 'renderer/renderer.dart';
28-
import 'span_frame_metrics_collector.dart';
29-
import 'utils/platform_dispatcher_wrapper.dart';
3028
import 'replay/integration.dart';
3129
import 'version.dart';
3230
import 'view_hierarchy/view_hierarchy_integration.dart';
@@ -68,8 +66,9 @@ mixin SentryFlutter {
6866
_native = createBinding(options);
6967
}
7068

71-
final platformDispatcher = PlatformDispatcher.instance;
72-
final wrapper = PlatformDispatcherWrapper(platformDispatcher);
69+
final wrapper = PlatformDispatcherWrapper(PlatformDispatcher.instance);
70+
final isMultiViewApp = wrapper.isMultiViewEnabled(options);
71+
options.isMultiViewApp = isMultiViewApp; // TODO @denis Make testable/mockable
7372

7473
// Flutter Web doesn't capture [Future] errors if using [PlatformDispatcher.onError] and not
7574
// the [runZonedGuarded].
@@ -87,8 +86,11 @@ mixin SentryFlutter {
8786

8887
// first step is to install the native integration and set default values,
8988
// so we are able to capture future errors.
90-
final defaultIntegrations =
91-
_createDefaultIntegrations(options, isOnErrorSupported);
89+
final defaultIntegrations = _createDefaultIntegrations(
90+
options,
91+
isOnErrorSupported,
92+
isMultiViewApp,
93+
);
9294
for (final defaultIntegration in defaultIntegrations) {
9395
options.addIntegration(defaultIntegration);
9496
}
@@ -157,6 +159,7 @@ mixin SentryFlutter {
157159
static List<Integration> _createDefaultIntegrations(
158160
SentryFlutterOptions options,
159161
bool isOnErrorSupported,
162+
bool isMultiViewApp,
160163
) {
161164
final integrations = <Integration>[];
162165
final platformChecker = options.platformChecker;
@@ -172,8 +175,10 @@ mixin SentryFlutter {
172175
// Will catch any errors that may occur in the Flutter framework itself.
173176
integrations.add(FlutterErrorIntegration());
174177

175-
// This tracks Flutter application events, such as lifecycle events.
176-
integrations.add(WidgetsBindingIntegration());
178+
if (!isMultiViewApp) {
179+
// This tracks Flutter application events, such as lifecycle events.
180+
integrations.add(WidgetsBindingIntegration());
181+
}
177182

178183
// The ordering here matters, as we'd like to first start the native integration.
179184
// That allow us to send events to the network and then the Flutter integrations.
@@ -198,7 +203,8 @@ mixin SentryFlutter {
198203
}
199204

200205
final renderer = options.rendererWrapper.getRenderer();
201-
if (!platformChecker.isWeb || renderer == FlutterRenderer.canvasKit) {
206+
if ((!platformChecker.isWeb || renderer == FlutterRenderer.canvasKit) &&
207+
!isMultiViewApp) {
202208
integrations.add(ScreenshotIntegration());
203209
}
204210

flutter/lib/src/sentry_flutter_options.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'dart:async';
2+
import 'dart:ui';
23

34
import 'package:file/file.dart';
45
import 'package:file/local.dart';
@@ -7,6 +8,7 @@ import 'package:flutter/widgets.dart';
78
import 'package:meta/meta.dart' as meta;
89
import 'package:sentry/sentry.dart';
910

11+
import '../sentry_flutter.dart';
1012
import 'binding_wrapper.dart';
1113
import 'event_processor/screenshot_event_processor.dart';
1214
import 'navigation/time_to_display_tracker.dart';
@@ -236,6 +238,9 @@ class SentryFlutterOptions extends SentryOptions {
236238
@meta.internal
237239
late RendererWrapper rendererWrapper = RendererWrapper();
238240

241+
@meta.internal
242+
bool isMultiViewApp = false;
243+
239244
@meta.internal
240245
late MethodChannel methodChannel = const MethodChannel('sentry_flutter');
241246

flutter/lib/src/sentry_widget.dart

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'package:flutter/cupertino.dart';
22
import 'package:meta/meta.dart';
33
import '../sentry_flutter.dart';
4-
import 'utils/multi_view_helper.dart';
54

65
/// Key which is used to identify the [SentryWidget]
76
@internal
@@ -11,14 +10,22 @@ final sentryWidgetGlobalKey = GlobalKey(debugLabel: 'sentry_widget');
1110
/// as [SentryScreenshotWidget] and [SentryUserInteractionWidget].
1211
class SentryWidget extends StatefulWidget {
1312
final Widget child;
13+
late final Hub _hub;
1414

1515
SentryWidget({
1616
super.key,
1717
required this.child,
1818
@internal Hub? hub,
19-
});
19+
}) {
20+
_hub = hub ?? HubAdapter();
21+
}
2022

21-
final bool _isMultiViewEnabled = MultiViewHelper.isMultiViewEnabled();
23+
SentryFlutterOptions? get _options =>
24+
// ignore: invalid_use_of_internal_member
25+
_hub.options is SentryFlutterOptions
26+
// ignore: invalid_use_of_internal_member
27+
? _hub.options as SentryFlutterOptions?
28+
: null;
2229

2330
@override
2431
_SentryWidgetState createState() => _SentryWidgetState();
@@ -28,19 +35,20 @@ class _SentryWidgetState extends State<SentryWidget> {
2835
@override
2936
Widget build(BuildContext context) {
3037
Widget content = widget.child;
31-
if (widget._isMultiViewEnabled) {
38+
if (widget._options?.isMultiViewApp ?? false) {
3239
// ignore: invalid_use_of_internal_member
3340
Sentry.currentHub.options.logger(
3441
SentryLevel.debug,
35-
'`SentryScreenshotWidget` and `SentryUserInteractionWidget` is not available in multi-view applications.',
42+
'`SentryWidget` is not available in multi-view apps.',
3643
);
3744
return content;
45+
} else {
46+
content = SentryScreenshotWidget(child: content);
47+
content = SentryUserInteractionWidget(child: content);
48+
return Container(
49+
key: sentryWidgetGlobalKey,
50+
child: content,
51+
);
3852
}
39-
content = SentryScreenshotWidget(child: content);
40-
content = SentryUserInteractionWidget(child: content);
41-
return Container(
42-
key: sentryWidgetGlobalKey,
43-
child: content,
44-
);
4553
}
4654
}

flutter/lib/src/utils/multi_view_helper.dart

Lines changed: 0 additions & 13 deletions
This file was deleted.

flutter/lib/src/utils/platform_dispatcher_wrapper.dart

Lines changed: 0 additions & 73 deletions
This file was deleted.

flutter/test/integrations/not_initialized_widgets_binding_on_error_integration_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import 'package:flutter_test/flutter_test.dart';
22
import 'package:mockito/mockito.dart';
33
import 'package:sentry/sentry.dart';
44
import 'package:sentry_flutter/src/integrations/on_error_integration.dart';
5-
import 'package:sentry_flutter/src/utils/platform_dispatcher_wrapper.dart';
65

76
import '../mocks.dart';
87
import '../mocks.mocks.dart';

flutter/test/integrations/on_error_integration_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import 'package:flutter_test/flutter_test.dart';
22
import 'package:mockito/mockito.dart';
33
import 'package:sentry/sentry.dart';
44
import 'package:sentry_flutter/src/integrations/on_error_integration.dart';
5-
import 'package:sentry_flutter/src/utils/platform_dispatcher_wrapper.dart';
65

76
import '../mocks.dart';
87
import '../mocks.mocks.dart';

flutter/test/sentry_flutter_test.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,8 @@ void main() {
732732
);
733733
});
734734
});
735+
736+
// TODO @denis Test isMultiViewApp...
735737
}
736738

737739
MockSentryNativeBinding mockNativeBinding() {

0 commit comments

Comments
 (0)