Skip to content

Commit 02419b7

Browse files
denrasemarandaneto
andauthored
Fix: Use PlatformDispatcher.onError in Flutter 3.3 (#1039)
Co-authored-by: Manoel Aranda Neto <[email protected]> Co-authored-by: Manoel Aranda Neto <[email protected]>
1 parent 4efee31 commit 02419b7

File tree

10 files changed

+366
-147
lines changed

10 files changed

+366
-147
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Features
6+
7+
- Use PlatformDispatcher.onError in Flutter 3.3 ([#1039](https://github.com/getsentry/sentry-dart/pull/1039))
8+
59
### Fixes
610

711
- Bring protocol up to date with latest Sentry protocol ([#1038](https://github.com/getsentry/sentry-dart/pull/1038))

dart/lib/src/sentry.dart

+26-16
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Sentry {
3636
static Future<void> init(
3737
OptionsConfiguration optionsConfiguration, {
3838
AppRunner? appRunner,
39+
@internal bool callAppRunnerInRunZonedGuarded = true,
3940
@internal SentryOptions? options,
4041
}) async {
4142
final sentryOptions = options ?? SentryOptions();
@@ -56,7 +57,7 @@ class Sentry {
5657
throw ArgumentError('DSN is required.');
5758
}
5859

59-
await _init(sentryOptions, appRunner);
60+
await _init(sentryOptions, appRunner, callAppRunnerInRunZonedGuarded);
6061
}
6162

6263
static Future<void> _initDefaultValues(
@@ -96,7 +97,11 @@ class Sentry {
9697
}
9798

9899
/// Initializes the SDK
99-
static Future<void> _init(SentryOptions options, AppRunner? appRunner) async {
100+
static Future<void> _init(
101+
SentryOptions options,
102+
AppRunner? appRunner,
103+
bool callAppRunnerInRunZonedGuarded,
104+
) async {
100105
if (isEnabled) {
101106
options.logger(
102107
SentryLevel.warning,
@@ -113,21 +118,26 @@ class Sentry {
113118

114119
// execute integrations after hub being enabled
115120
if (appRunner != null) {
116-
var runIntegrationsAndAppRunner = () async {
117-
final integrations =
118-
options.integrations.where((i) => i is! RunZonedGuardedIntegration);
119-
await _callIntegrations(integrations, options);
121+
if (callAppRunnerInRunZonedGuarded) {
122+
var runIntegrationsAndAppRunner = () async {
123+
final integrations = options.integrations
124+
.where((i) => i is! RunZonedGuardedIntegration);
125+
await _callIntegrations(integrations, options);
126+
await appRunner();
127+
};
128+
129+
final runZonedGuardedIntegration =
130+
RunZonedGuardedIntegration(runIntegrationsAndAppRunner);
131+
options.addIntegrationByIndex(0, runZonedGuardedIntegration);
132+
133+
// RunZonedGuardedIntegration will run other integrations and appRunner
134+
// runZonedGuarded so all exception caught in the error handler are
135+
// handled
136+
await runZonedGuardedIntegration(HubAdapter(), options);
137+
} else {
138+
await _callIntegrations(options.integrations, options);
120139
await appRunner();
121-
};
122-
123-
final runZonedGuardedIntegration =
124-
RunZonedGuardedIntegration(runIntegrationsAndAppRunner);
125-
options.addIntegrationByIndex(0, runZonedGuardedIntegration);
126-
127-
// RunZonedGuardedIntegration will run other integrations and appRunner
128-
// runZonedGuarded so all exception caught in the error handler are
129-
// handled
130-
await runZonedGuardedIntegration(HubAdapter(), options);
140+
}
131141
} else {
132142
await _callIntegrations(options.integrations, options);
133143
}

dart/test/sentry_test.dart

+25
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,31 @@ void main() {
284284
});
285285
});
286286

287+
test('should complete when appRunner is not called in runZonedGuarded',
288+
() async {
289+
final completer = Completer();
290+
var completed = false;
291+
292+
final init = Sentry.init(
293+
(options) {
294+
options.dsn = fakeDsn;
295+
},
296+
appRunner: () => completer.future,
297+
callAppRunnerInRunZonedGuarded: false,
298+
).whenComplete(() => completed = true);
299+
300+
await Future(() {
301+
// We make the expectation only after all microtasks have completed,
302+
// that Sentry.init might have scheduled.
303+
expect(completed, false);
304+
});
305+
306+
completer.complete();
307+
await init;
308+
309+
expect(completed, true);
310+
});
311+
287312
test('options.environment debug', () async {
288313
final sentryOptions = SentryOptions(dsn: fakeDsn)
289314
..platformChecker = FakePlatformChecker.debugMode();

flutter/example/lib/main.dart

-14
Original file line numberDiff line numberDiff line change
@@ -187,20 +187,6 @@ class MainScaffold extends StatelessWidget {
187187
},
188188
child: const Text('Capture from FlutterError.onError'),
189189
),
190-
ElevatedButton(
191-
onPressed: () {
192-
// Only usable on Flutter >= 3.3
193-
// and needs the following additional setup:
194-
// options.addIntegration(OnErrorIntegration());
195-
(WidgetsBinding.instance.platformDispatcher as dynamic)
196-
.onError
197-
?.call(
198-
Exception('PlatformDispatcher.onError'),
199-
StackTrace.current,
200-
);
201-
},
202-
child: const Text('Capture from PlatformDispatcher.onError'),
203-
),
204190
ElevatedButton(
205191
onPressed: () => makeWebRequest(context),
206192
child: const Text('Dart: Web request'),

flutter/example/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ dependencies:
2424

2525
dev_dependencies:
2626
pedantic: ^1.11.1
27-
sentry_dart_plugin: ^1.0.0-alpha.4
27+
sentry_dart_plugin: ^1.0.0-beta.1
2828

2929
dependency_overrides:
3030
sentry:

flutter/lib/src/integrations/on_error_integration.dart

-4
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,6 @@ class OnErrorIntegration implements Integration<SentryFlutterOptions> {
3838
// WidgetsBinding works with WidgetsFlutterBinding and other custom bindings
3939
final wrapper = dispatchWrapper ??
4040
PlatformDispatcherWrapper(binding.platformDispatcher);
41-
42-
if (!wrapper.isOnErrorSupported(options)) {
43-
return;
44-
}
4541
_defaultOnError = wrapper.onError;
4642

4743
_integrationOnError = (Object exception, StackTrace stackTrace) {

flutter/lib/src/sentry_flutter.dart

+15-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import 'dart:async';
2+
import 'dart:ui';
23

34
import 'package:flutter/scheduler.dart';
45
import 'package:flutter/services.dart';
56
import 'package:meta/meta.dart';
67
import 'package:package_info_plus/package_info_plus.dart';
7-
import 'package:sentry/sentry.dart';
8+
import '../sentry_flutter.dart';
89
import 'event_processor/android_platform_exception_event_processor.dart';
910
import 'native_scope_observer.dart';
1011
import 'sentry_native.dart';
@@ -13,9 +14,7 @@ import 'sentry_native_channel.dart';
1314
import 'event_processor/flutter_enricher_event_processor.dart';
1415
import 'integrations/debug_print_integration.dart';
1516
import 'integrations/native_app_start_integration.dart';
16-
import 'sentry_flutter_options.dart';
1717

18-
import 'default_integrations.dart';
1918
import 'file_system_transport.dart';
2019

2120
import 'version.dart';
@@ -45,12 +44,17 @@ mixin SentryFlutter {
4544
final native = SentryNative();
4645
native.setNativeChannel(nativeChannel);
4746

47+
final platformDispatcher = PlatformDispatcher.instance;
48+
final wrapper = PlatformDispatcherWrapper(platformDispatcher);
49+
final isOnErrorSupported = wrapper.isOnErrorSupported(flutterOptions);
50+
4851
// first step is to install the native integration and set default values,
4952
// so we are able to capture future errors.
5053
final defaultIntegrations = _createDefaultIntegrations(
5154
packageLoader,
5255
channel,
5356
flutterOptions,
57+
isOnErrorSupported,
5458
);
5559
for (final defaultIntegration in defaultIntegrations) {
5660
flutterOptions.addIntegration(defaultIntegration);
@@ -65,6 +69,8 @@ mixin SentryFlutter {
6569
appRunner: appRunner,
6670
// ignore: invalid_use_of_internal_member
6771
options: flutterOptions,
72+
// ignore: invalid_use_of_internal_member
73+
callAppRunnerInRunZonedGuarded: !isOnErrorSupported,
6874
);
6975
}
7076

@@ -96,6 +102,7 @@ mixin SentryFlutter {
96102
PackageLoader packageLoader,
97103
MethodChannel channel,
98104
SentryFlutterOptions options,
105+
bool isOnErrorSupported,
99106
) {
100107
final integrations = <Integration>[];
101108
final platformChecker = options.platformChecker;
@@ -104,6 +111,11 @@ mixin SentryFlutter {
104111
// Will call WidgetsFlutterBinding.ensureInitialized() before all other integrations.
105112
integrations.add(WidgetsFlutterBindingIntegration());
106113

114+
// Use PlatformDispatcher.onError instead of zones.
115+
if (isOnErrorSupported) {
116+
integrations.add(OnErrorIntegration());
117+
}
118+
107119
// Will catch any errors that may occur in the Flutter framework itself.
108120
integrations.add(FlutterErrorIntegration());
109121

flutter/test/mocks.mocks.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Mocks generated by Mockito 5.3.1 from annotations
1+
// Mocks generated by Mockito 5.3.2 from annotations
22
// in sentry_flutter/example/windows/flutter/ephemeral/.plugin_symlinks/sentry_flutter/example/linux/flutter/ephemeral/.plugin_symlinks/sentry_flutter/example/ios/.symlinks/plugins/sentry_flutter/test/mocks.dart.
33
// Do not manually edit this file.
44

0 commit comments

Comments
 (0)