@@ -294,24 +294,50 @@ class _SentryUserInteractionWidgetState
294
294
}
295
295
296
296
void _onPointerDown (PointerDownEvent event) {
297
- _lastPointerId = event.pointer;
298
- _lastPointerDownLocation = event.localPosition;
297
+ try {
298
+ _lastPointerId = event.pointer;
299
+ _lastPointerDownLocation = event.localPosition;
300
+ } catch (exception, stacktrace) {
301
+ _options? .logger (
302
+ SentryLevel .error,
303
+ 'Error while handling pointer-down event $event in $SentryUserInteractionWidget ' ,
304
+ exception: exception,
305
+ stackTrace: stacktrace,
306
+ );
307
+ // ignore: invalid_use_of_internal_member
308
+ if (_options? .automatedTestMode ?? false ) {
309
+ rethrow ;
310
+ }
311
+ }
299
312
}
300
313
301
314
void _onPointerUp (PointerUpEvent event) {
302
- // Figure out if something was tapped
303
- final location = _lastPointerDownLocation;
304
- if (location == null || event.pointer != _lastPointerId) {
305
- return ;
306
- }
307
- final delta = Offset (
308
- location.dx - event.localPosition.dx,
309
- location.dy - event.localPosition.dy,
310
- );
315
+ try {
316
+ // Figure out if something was tapped
317
+ final location = _lastPointerDownLocation;
318
+ if (location == null || event.pointer != _lastPointerId) {
319
+ return ;
320
+ }
321
+ final delta = Offset (
322
+ location.dx - event.localPosition.dx,
323
+ location.dy - event.localPosition.dy,
324
+ );
311
325
312
- if (delta.distanceSquared < _tapDeltaArea) {
313
- // Widget was tapped
314
- _onTappedAt (event.localPosition);
326
+ if (delta.distanceSquared < _tapDeltaArea) {
327
+ // Widget was tapped
328
+ _onTappedAt (event.localPosition);
329
+ }
330
+ } catch (exception, stacktrace) {
331
+ _options? .logger (
332
+ SentryLevel .error,
333
+ 'Error while handling pointer-up event $event in $SentryUserInteractionWidget ' ,
334
+ exception: exception,
335
+ stackTrace: stacktrace,
336
+ );
337
+ // ignore: invalid_use_of_internal_member
338
+ if (_options? .automatedTestMode ?? false ) {
339
+ rethrow ;
340
+ }
315
341
}
316
342
}
317
343
@@ -331,11 +357,11 @@ class _SentryUserInteractionWidgetState
331
357
return ;
332
358
}
333
359
334
- Map < String , dynamic > ? data = {} ;
335
- final description = _findDescriptionOf (info.element);
336
- if (description.isNotEmpty) {
337
- data[ ' label' ] = description;
338
- }
360
+ final label = _getLabelRecursively (info.element) ;
361
+ final data = {
362
+ 'path' : _getTouchPath (info.element),
363
+ if ( label != null ) 'label' : label
364
+ };
339
365
340
366
final crumb = Breadcrumb .userInteraction (
341
367
subCategory: 'click' ,
@@ -347,6 +373,37 @@ class _SentryUserInteractionWidgetState
347
373
_hub.addBreadcrumb (crumb, hint: hint);
348
374
}
349
375
376
+ List <Map <String , String ?>> _getTouchPath (Element element) {
377
+ final path = < Map <String , String ?>> [];
378
+
379
+ bool addToPath (Element element) {
380
+ // Break at the boundary (i.e. this [SentryUserInteractionWidget]).
381
+ if (element.widget == widget) {
382
+ return false ;
383
+ }
384
+
385
+ final widgetName = element.widget.toStringShort ();
386
+ if (! widgetName.startsWith ('_' )) {
387
+ final info = {
388
+ 'name' : WidgetUtils .toStringValue (element.widget.key),
389
+ 'element' : _getElementType (element) ?? widgetName,
390
+ 'label' : _getLabel (element, true ),
391
+ }..removeWhere ((key, value) => value == null );
392
+ if (info.isNotEmpty) {
393
+ path.add (info);
394
+ }
395
+ }
396
+
397
+ return path.length < 10 ;
398
+ }
399
+
400
+ if (addToPath (element)) {
401
+ element.visitAncestorElements (addToPath);
402
+ }
403
+
404
+ return path;
405
+ }
406
+
350
407
void _startTransactionOnTap (UserInteractionInfo info, String ? widgetKey) {
351
408
if (widgetKey == null ||
352
409
! (_options? .isTracingEnabled () ?? false ) ||
@@ -416,8 +473,31 @@ class _SentryUserInteractionWidgetState
416
473
});
417
474
}
418
475
419
- String _findDescriptionOf (Element element) {
420
- var description = '' ;
476
+ String ? _getLabel (Element element, bool allowText) {
477
+ String ? label;
478
+
479
+ if (_options? .sendDefaultPii ?? false ) {
480
+ final widget = element.widget;
481
+ if (allowText && widget is Text ) {
482
+ label = widget.data;
483
+ } else if (widget is Semantics ) {
484
+ label = widget.properties.label;
485
+ } else if (widget is Icon ) {
486
+ label = widget.semanticLabel;
487
+ } else if (widget is Tooltip ) {
488
+ label = widget.message;
489
+ }
490
+
491
+ if (label? .isEmpty ?? true ) {
492
+ label = null ;
493
+ }
494
+ }
495
+
496
+ return label;
497
+ }
498
+
499
+ String ? _getLabelRecursively (Element element) {
500
+ String ? label;
421
501
422
502
if (_options? .sendDefaultPii ?? false ) {
423
503
final widget = element.widget;
@@ -427,36 +507,16 @@ class _SentryUserInteractionWidgetState
427
507
428
508
// traverse tree to find a suiting element
429
509
void descriptionFinder (Element element) {
430
- bool foundDescription = false ;
431
-
432
- final widget = element.widget;
433
- if (allowText && widget is Text ) {
434
- final data = widget.data;
435
- if (data != null && data.isNotEmpty) {
436
- description = data;
437
- foundDescription = true ;
438
- }
439
- } else if (widget is Semantics ) {
440
- if (widget.properties.label? .isNotEmpty ?? false ) {
441
- description = widget.properties.label! ;
442
- foundDescription = true ;
443
- }
444
- } else if (widget is Icon ) {
445
- if (widget.semanticLabel? .isNotEmpty ?? false ) {
446
- description = widget.semanticLabel! ;
447
- foundDescription = true ;
448
- }
449
- }
450
-
451
- if (! foundDescription) {
510
+ label ?? = _getLabel (element, allowText);
511
+ if (label == null ) {
452
512
element.visitChildren (descriptionFinder);
453
513
}
454
514
}
455
515
456
- element. visitChildren ( descriptionFinder);
516
+ descriptionFinder (element );
457
517
}
458
518
459
- return description ;
519
+ return label ;
460
520
}
461
521
462
522
UserInteractionInfo ? _getElementAt (Offset position) {
0 commit comments