Skip to content

Commit 6034b0a

Browse files
denrasebuenaflor
andauthored
Add textScale(r) value to Flutter context (#1886)
* entrich event contexts app with text scale * read textScale from sentry widget event processor * revert change * revert change * update changelog * update changelog * Use scale method instead of deprecated `textScaleFactor` * fix changelog * fix changelog * use maybeTextScaleFactorOf method * fix changelog * fix changelog * use textScaleFactorOf * fix tests * update doc * Update CHANGELOG.md --------- Co-authored-by: Giancarlo Buenaflor <[email protected]>
1 parent be8cafe commit 6034b0a

File tree

8 files changed

+110
-5
lines changed

8 files changed

+110
-5
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Features
6+
7+
- Add textScale(r) value to Flutter context ([#1886](https://github.com/getsentry/sentry-dart/pull/1886))
8+
59
### Dependencies
610

711
- Bump Android SDK from v7.6.0 to v7.8.0 ([#1977](https://github.com/getsentry/sentry-dart/pull/1977))

dart/lib/src/protocol/sentry_app.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class SentryApp {
1919
this.appMemory,
2020
this.inForeground,
2121
this.viewNames,
22+
this.textScale,
2223
});
2324

2425
/// Human readable application name, as it appears on the platform.
@@ -52,6 +53,9 @@ class SentryApp {
5253
/// The names of the currently visible views.
5354
final List<String>? viewNames;
5455

56+
/// The current text scale. Only available on Flutter.
57+
final double? textScale;
58+
5559
/// Deserializes a [SentryApp] from JSON [Map].
5660
factory SentryApp.fromJson(Map<String, dynamic> data) {
5761
final viewNamesJson = data['view_names'] as List<dynamic>?;
@@ -68,6 +72,7 @@ class SentryApp {
6872
appMemory: data['app_memory'],
6973
inForeground: data['in_foreground'],
7074
viewNames: viewNamesJson?.map((e) => e as String).toList(),
75+
textScale: data['text_scale'],
7176
);
7277
}
7378

@@ -84,6 +89,7 @@ class SentryApp {
8489
if (appMemory != null) 'app_memory': appMemory!,
8590
if (inForeground != null) 'in_foreground': inForeground!,
8691
if (viewNames != null && viewNames!.isNotEmpty) 'view_names': viewNames!,
92+
if (textScale != null) 'text_scale': textScale!,
8793
};
8894
}
8995

@@ -98,6 +104,7 @@ class SentryApp {
98104
appMemory: appMemory,
99105
inForeground: inForeground,
100106
viewNames: viewNames,
107+
textScale: textScale,
101108
);
102109

103110
SentryApp copyWith({
@@ -111,6 +118,7 @@ class SentryApp {
111118
int? appMemory,
112119
bool? inForeground,
113120
List<String>? viewNames,
121+
double? textScale,
114122
}) =>
115123
SentryApp(
116124
name: name ?? this.name,
@@ -123,5 +131,6 @@ class SentryApp {
123131
appMemory: appMemory ?? this.appMemory,
124132
inForeground: inForeground ?? this.inForeground,
125133
viewNames: viewNames ?? this.viewNames,
134+
textScale: textScale ?? this.textScale,
126135
);
127136
}

dart/test/protocol/sentry_app_test.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ void main() {
1515
deviceAppHash: 'fixture-deviceAppHash',
1616
inForeground: true,
1717
viewNames: ['fixture-viewName', 'fixture-viewName2'],
18+
textScale: 2.0,
1819
);
1920

2021
final sentryAppJson = <String, dynamic>{
@@ -27,6 +28,7 @@ void main() {
2728
'device_app_hash': 'fixture-deviceAppHash',
2829
'in_foreground': true,
2930
'view_names': ['fixture-viewName', 'fixture-viewName2'],
31+
'text_scale': 2.0,
3032
};
3133

3234
group('json', () {
@@ -42,6 +44,7 @@ void main() {
4244
expect(json['device_app_hash'], 'fixture-deviceAppHash');
4345
expect(json['in_foreground'], true);
4446
expect(json['view_names'], ['fixture-viewName', 'fixture-viewName2']);
47+
expect(json['text_scale'], 2.0);
4548
});
4649
test('fromJson', () {
4750
final sentryApp = SentryApp.fromJson(sentryAppJson);
@@ -56,6 +59,7 @@ void main() {
5659
expect(json['device_app_hash'], 'fixture-deviceAppHash');
5760
expect(json['in_foreground'], true);
5861
expect(json['view_names'], ['fixture-viewName', 'fixture-viewName2']);
62+
expect(json['text_scale'], 2.0);
5963
});
6064
});
6165

@@ -86,6 +90,7 @@ void main() {
8690
deviceAppHash: 'hash1',
8791
inForeground: true,
8892
viewNames: ['screen1'],
93+
textScale: 3.0,
8994
);
9095

9196
expect('name1', copy.name);
@@ -97,6 +102,7 @@ void main() {
97102
expect('hash1', copy.deviceAppHash);
98103
expect(true, copy.inForeground);
99104
expect(['screen1'], copy.viewNames);
105+
expect(3.0, copy.textScale);
100106
});
101107
});
102108
}

flutter/example/lib/main.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ Future<void> setupSentry(
8888

8989
options.maxRequestBodySize = MaxRequestBodySize.always;
9090
options.maxResponseBodySize = MaxResponseBodySize.always;
91+
options.navigatorKey = navigatorKey;
9192

9293
_isIntegrationTest = isIntegrationTest;
9394
if (_isIntegrationTest) {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import 'dart:async';
2+
3+
import 'package:flutter/widgets.dart';
4+
5+
import '../../sentry_flutter.dart';
6+
7+
class WidgetEventProcessor implements EventProcessor {
8+
@override
9+
FutureOr<SentryEvent?> apply(SentryEvent event, {Hint? hint}) {
10+
if (event is SentryTransaction) {
11+
return event;
12+
}
13+
if (event.exceptions == null && event.throwable == null) {
14+
return event;
15+
}
16+
final context = sentryWidgetGlobalKey.currentContext;
17+
if (context == null) {
18+
return event;
19+
}
20+
21+
// ignore: deprecated_member_use
22+
final textScale = MediaQuery.textScaleFactorOf(context);
23+
return event.copyWith(
24+
contexts: event.contexts.copyWith(
25+
app: event.contexts.app?.copyWith(
26+
textScale: textScale,
27+
),
28+
),
29+
);
30+
}
31+
}

flutter/lib/src/sentry_flutter.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import '../sentry_flutter.dart';
88
import 'event_processor/android_platform_exception_event_processor.dart';
99
import 'event_processor/flutter_exception_event_processor.dart';
1010
import 'event_processor/platform_exception_event_processor.dart';
11+
import 'event_processor/widget_event_processor.dart';
1112
import 'frame_callback_handler.dart';
1213
import 'integrations/connectivity/connectivity_integration.dart';
1314
import 'integrations/screenshot_integration.dart';
@@ -110,12 +111,13 @@ mixin SentryFlutter {
110111
options.addScopeObserver(NativeScopeObserver(_native!));
111112
}
112113

113-
var flutterEventProcessor = FlutterEnricherEventProcessor(options);
114-
options.addEventProcessor(flutterEventProcessor);
114+
options.addEventProcessor(FlutterEnricherEventProcessor(options));
115+
options.addEventProcessor(WidgetEventProcessor());
115116

116117
if (options.platformChecker.platform.isAndroid) {
117-
options
118-
.addEventProcessor(AndroidPlatformExceptionEventProcessor(options));
118+
options.addEventProcessor(
119+
AndroidPlatformExceptionEventProcessor(options),
120+
);
119121
}
120122

121123
options.addEventProcessor(PlatformExceptionEventProcessor());

flutter/lib/src/sentry_widget.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import 'package:flutter/cupertino.dart';
2+
import 'package:meta/meta.dart';
23
import '../sentry_flutter.dart';
34

5+
/// Key which is used to identify the [SentryWidget]
6+
@internal
7+
final sentryWidgetGlobalKey = GlobalKey(debugLabel: 'sentry_widget');
8+
49
/// This widget serves as a wrapper to include Sentry widgets such
510
/// as [SentryScreenshotWidget] and [SentryUserInteractionWidget].
611
class SentryWidget extends StatefulWidget {
@@ -18,6 +23,9 @@ class _SentryWidgetState extends State<SentryWidget> {
1823
Widget content = widget.child;
1924
content = SentryScreenshotWidget(child: content);
2025
content = SentryUserInteractionWidget(child: content);
21-
return content;
26+
return Container(
27+
key: sentryWidgetGlobalKey,
28+
child: content,
29+
);
2230
}
2331
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import 'package:flutter/widgets.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
import 'package:sentry_flutter/sentry_flutter.dart';
4+
import 'package:sentry_flutter/src/event_processor/widget_event_processor.dart';
5+
6+
void main() {
7+
TestWidgetsFlutterBinding.ensureInitialized();
8+
late Fixture fixture;
9+
10+
setUp(() {
11+
fixture = Fixture();
12+
});
13+
14+
testWidgets('adds screenshot attachment dart:io', (tester) async {
15+
await tester.runAsync(() async {
16+
final sut = fixture.getSut();
17+
await tester.pumpWidget(
18+
SentryWidget(
19+
child: Text(
20+
'Catching Pokémon is a snap!',
21+
textDirection: TextDirection.ltr,
22+
),
23+
),
24+
);
25+
26+
final throwable = Exception();
27+
SentryEvent? event = SentryEvent(throwable: throwable);
28+
event = event.copyWith(
29+
contexts: event.contexts.copyWith(
30+
app: SentryApp(),
31+
),
32+
);
33+
event = await sut.apply(event);
34+
35+
expect(event?.contexts.app?.textScale, 1.0);
36+
});
37+
});
38+
}
39+
40+
class Fixture {
41+
WidgetEventProcessor getSut() {
42+
return WidgetEventProcessor();
43+
}
44+
}

0 commit comments

Comments
 (0)