Skip to content

Commit 6198568

Browse files
authored
Use new PlatformDispatcher onError method if available (#109)
* Use new PlatformDispatcher onError method if available * Updated Flutter Demo with more types of possible uncaught errors
1 parent daba1fc commit 6198568

File tree

5 files changed

+118
-36
lines changed

5 files changed

+118
-36
lines changed

rollbar_flutter/example/lib/main.dart

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class MyHomePageState extends State<MyHomePage> {
104104

105105
setState(() {
106106
if (++_counter % 2 == 0) {
107-
throw ArgumentError('Unavoidable failure');
107+
throw ArgumentError('Failed to increment counter');
108108
} else {
109109
Rollbar.drop(
110110
rollbar.Breadcrumb.log('Counter incremented to $_counter'),
@@ -113,6 +113,14 @@ class MyHomePageState extends State<MyHomePage> {
113113
});
114114
}
115115

116+
void asyncFailure() {
117+
_asyncFailure(1).then((n) => log('$n ~/ 0 = ???'));
118+
}
119+
120+
Future<int> _asyncFailure(int num) async {
121+
return Future<int>.delayed(Duration(seconds: num), () => num ~/ 0);
122+
}
123+
116124
void setUser() {
117125
if (_userIsLoggedIn) {
118126
Rollbar.setUser(null);
@@ -133,10 +141,10 @@ class MyHomePageState extends State<MyHomePage> {
133141
});
134142
}
135143

136-
void divideByZero() {
137-
Rollbar.drop(rollbar.Breadcrumb.log('Tapped divideByZero button'));
138-
Rollbar.critical('About to divide by zero, this won\'t work!');
139-
1 ~/ 0;
144+
void throwError() {
145+
Rollbar.drop(rollbar.Breadcrumb.log('Tapped throwError button'));
146+
Rollbar.critical('About to throw an error!');
147+
throw StateError('A state error occurred');
140148
}
141149

142150
void crash() {
@@ -157,10 +165,6 @@ class MyHomePageState extends State<MyHomePage> {
157165
child: const Text('Call Faulty Method'),
158166
),
159167
Text(_faultyMsg),
160-
ElevatedButton(
161-
onPressed: divideByZero,
162-
child: const Text('Divide by zero'),
163-
),
164168
if (Platform.isIOS)
165169
ElevatedButton(
166170
onPressed: crash,
@@ -172,6 +176,15 @@ class MyHomePageState extends State<MyHomePage> {
172176
child: Text(_setUserText),
173177
),
174178
const Divider(),
179+
ElevatedButton(
180+
onPressed: asyncFailure,
181+
child: const Text('Async failure'),
182+
),
183+
ElevatedButton(
184+
onPressed: throwError,
185+
child: const Text('Throw error'),
186+
),
187+
const Divider(),
175188
const Text('Times you have pushed the plus button:'),
176189
Text(
177190
'$_counter',

rollbar_flutter/lib/src/flutter_error.dart renamed to rollbar_flutter/lib/src/hooks/flutter_hook.dart

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import 'package:flutter/foundation.dart';
2+
import 'package:meta/meta.dart';
23

34
import 'package:rollbar_common/rollbar_common.dart';
45
import 'package:rollbar_dart/rollbar.dart';
56

6-
import 'extension/diagnostics.dart';
7+
import '../extension/diagnostics.dart';
8+
import 'hook.dart';
79

8-
extension RollbarFlutterError on FlutterError {
9-
/// Called whenever the Flutter framework catches an error.
10-
///
11-
/// The default behavior is to call [presentError].
12-
static void onError(FlutterErrorDetails error) {
10+
@sealed
11+
class FlutterHook implements Hook {
12+
FlutterExceptionHandler? _originalOnError;
13+
14+
void onError(FlutterErrorDetails error) {
1315
if (!error.silent) {
1416
Rollbar.drop(
1517
Breadcrumb.error(
@@ -27,6 +29,22 @@ extension RollbarFlutterError on FlutterError {
2729
Rollbar.error(error.exception, error.stack ?? StackTrace.empty);
2830
}
2931

30-
FlutterError.presentError(error);
32+
if (_originalOnError != null) {
33+
_originalOnError!(error);
34+
}
35+
}
36+
37+
@override
38+
void install(_) {
39+
_originalOnError = FlutterError.onError;
40+
FlutterError.onError = onError;
41+
}
42+
43+
@override
44+
void uninstall() {
45+
if (FlutterError.onError == onError) {
46+
FlutterError.onError = _originalOnError;
47+
_originalOnError = null;
48+
}
3149
}
3250
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import 'dart:async';
2+
3+
import 'package:rollbar_dart/rollbar.dart';
4+
5+
abstract class Hook {
6+
FutureOr<void> install(final Config config);
7+
8+
FutureOr<void> uninstall();
9+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import 'dart:ui';
2+
import 'package:rollbar_dart/rollbar.dart';
3+
import 'hook.dart';
4+
5+
class PlatformHook implements Hook {
6+
ErrorCallback? _originalOnError;
7+
PlatformDispatcher? _platformDispatcher;
8+
9+
bool onError(Object exception, StackTrace stackTrace) {
10+
Rollbar.error(exception, stackTrace);
11+
12+
if (_originalOnError != null) {
13+
return _originalOnError!(exception, stackTrace);
14+
}
15+
16+
return false;
17+
}
18+
19+
@override
20+
void install(_) {
21+
_platformDispatcher = PlatformDispatcher.instance;
22+
_originalOnError = _platformDispatcher?.onError;
23+
_platformDispatcher?.onError = onError;
24+
}
25+
26+
@override
27+
void uninstall() {
28+
if (_platformDispatcher?.onError == onError) {
29+
_platformDispatcher?.onError = _originalOnError;
30+
_originalOnError = null;
31+
_platformDispatcher = null;
32+
}
33+
}
34+
}

rollbar_flutter/lib/src/rollbar.dart

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import 'package:meta/meta.dart';
77

88
import 'package:rollbar_dart/rollbar.dart';
99

10-
import 'flutter_error.dart';
10+
import 'hooks/hook.dart';
11+
import 'hooks/flutter_hook.dart';
12+
import 'hooks/platform_hook.dart';
1113
import 'platform_transformer.dart';
1214

1315
extension _Methods on MethodChannel {
@@ -22,48 +24,54 @@ extension _Methods on MethodChannel {
2224
typedef RollbarClosure = FutureOr<void> Function();
2325

2426
@sealed
27+
@immutable
2528
class RollbarFlutter {
2629
static const _platform = MethodChannel('com.rollbar.flutter');
2730

28-
RollbarFlutter._();
31+
const RollbarFlutter._();
2932

3033
static Future<void> run(
3134
Config config,
3235
RollbarClosure appRunner,
3336
) async {
3437
if (!config.handleUncaughtErrors) {
35-
WidgetsFlutterBinding.ensureInitialized();
36-
37-
await _run(config, appRunner, null);
38-
39-
return;
38+
await _run(config, appRunner);
39+
} else if (requiresCustomZone) {
40+
await runZonedGuarded(
41+
() async => await _run(config, appRunner, [FlutterHook()]),
42+
Rollbar.error);
43+
} else {
44+
await _run(config, appRunner, [FlutterHook(), PlatformHook()]);
4045
}
41-
42-
await runZonedGuarded(() async {
43-
WidgetsFlutterBinding.ensureInitialized();
44-
45-
await _run(config, appRunner, RollbarFlutterError.onError);
46-
}, (exception, stackTrace) {
47-
Rollbar.error(exception, stackTrace);
48-
});
4946
}
5047

5148
static Future<void> _run(
5249
Config config,
53-
RollbarClosure appRunner,
54-
FlutterExceptionHandler? onError,
55-
) async {
50+
RollbarClosure appRunner, [
51+
List<Hook> hooks = const [],
52+
]) async {
53+
WidgetsFlutterBinding.ensureInitialized();
54+
5655
await Rollbar.run(config.copyWith(
5756
framework: 'flutter',
5857
persistencePath: await _platform.persistencePath,
5958
transformer: (_) => PlatformTransformer(),
6059
));
6160

62-
if (onError != null) {
63-
FlutterError.onError = onError;
61+
for (final hook in hooks) {
62+
await hook.install(config);
6463
}
6564

6665
await _platform.initialize(config: config);
6766
await appRunner();
6867
}
68+
69+
static bool get requiresCustomZone {
70+
try {
71+
(PlatformDispatcher.instance as dynamic)?.onError;
72+
return false;
73+
} on NoSuchMethodError {
74+
return true;
75+
}
76+
}
6977
}

0 commit comments

Comments
 (0)