Skip to content

feat(flutter): Track taps on custom widget types - #4069

Merged
buenaflor merged 6 commits into
getsentry:mainfrom
dawid-niedzwiecki:dawid-niedzwiecki/feat/material-ui-user-interaction
Oct 30, 2026
Merged

buenaflor merged 6 commits into
getsentry:mainfrom
dawid-niedzwiecki:dawid-niedzwiecki/feat/material-ui-user-interaction

Conversation

@dawid-niedzwiecki

@dawid-niedzwiecki dawid-niedzwiecki commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

📜 Description

Adds addUserInteractionWidget<T>(name, isEnabled:) to SentryFlutterOptions so apps can track taps on widgets the SDK does not recognize. A tap on a T (or a subclass) adds a ui.click breadcrumb and starts ui.action.click tracing, like the built-in widgets. name is a plain string, so view.class stays readable in minified and obfuscated builds.

  • Added types follow the same label rules as the built-in ones: labels need sendDefaultPii, and Text inside the widget counts.
  • Tooltip labels now come from RawTooltip.semanticsTooltip. Both the Flutter and the material_ui Tooltip build a RawTooltip, so material_ui tooltips work without setup.
  • Built-in widget types are checked first. A callback that throws is logged and treated as no match. Adding a type records the userInteractionWidgetTypes SDK feature.
  • The tooltip change also applies to built-in widgets: no label when excludeFromSemantics is set or TooltipVisibility hides tooltips, and richMessage tooltips now give one.
Setup for material_ui and cupertino_ui
import 'package:cupertino_ui/cupertino_ui.dart';
import 'package:material_ui/material_ui.dart';

options
  ..addUserInteractionWidget<ButtonStyleButton>('ButtonStyleButton',
      isEnabled: (widget) => widget.enabled)
  ..addUserInteractionWidget<MaterialButton>('MaterialButton',
      isEnabled: (widget) => widget.enabled)
  ..addUserInteractionWidget<CupertinoButton>('CupertinoButton',
      isEnabled: (widget) => widget.enabled)
  ..addUserInteractionWidget<InkWell>('InkWell',
      isEnabled: (widget) => widget.onTap != null)
  ..addUserInteractionWidget<IconButton>('IconButton',
      isEnabled: (widget) => widget.onPressed != null)
  ..addUserInteractionWidget<PopupMenuButton<Object?>>('PopupMenuButton',
      isEnabled: (widget) => widget.enabled)
  ..addUserInteractionWidget<PopupMenuItem<Object?>>('PopupMenuItem',
      isEnabled: (widget) => widget.enabled);

💡 Motivation and Context

Flutter is moving Material and Cupertino out of the SDK into material_ui and cupertino_ui. The classes keep their names but are different types, so SentryUserInteractionWidget matches none of them. Apps migrated with dart fix --code=migrate_design_widgets lose all tap breadcrumbs and click spans (#4022).

The SDK can't import these packages without raising the minimum Flutter version to 3.47, and type names are not reliable in release builds. So this adds an extension point instead, in the style of options.privacy.mask<T>().

Relates to #4048 and #4022. #3773 (more built-in types) and #3772 (Text labels for all types) are related but separate.

💚 How did you test it?

  • Widget tests pump real material_ui 1.6.0 and cupertino_ui 1.1.2 widgets, added as dev dependencies only: names, text and tooltip labels, disabled buttons, idle spans, and no crumb by default.
  • Test-only widgets that share SDK names cover sendDefaultPii off and a throwing callback. Unit tests for the type registry.
  • flutter test, dart analyze --fatal-infos, dart format and dart doc --dry-run pass on Flutter 3.47.7. The new dev dependencies need Flutter 3.47, which matches .fvmrc.

📝 Checklist

  • I reviewed submitted code
  • I added tests to verify changes
  • No new PII added or SDK only sends newly added PII if sendDefaultPii is enabled
  • I updated the docs if needed
  • All tests passing
  • Public API changes reviewed by another Mobile SDK team member or implemented according to the develop docs spec
  • No breaking changes

🔮 Next steps

  • Document the material_ui setup on the user interaction docs page.
  • Once the minimum Flutter version allows it, the SDK could recognize material_ui and cupertino_ui types out of the box.

Changelog Entry

Add addUserInteractionWidget to SentryFlutterOptions to track taps on widgets the SDK doesn't recognize, like those from material_ui and cupertino_ui. Tooltip labels now come from RawTooltip, so material_ui tooltips get labels too.

dawid-niedzwiecki and others added 2 commits October 9, 2026 18:17
Add SentryFlutterOptions.addUserInteractionWidget<T>() and
addUserInteractionLabel<T>() so apps can name tappable widgets and
label sources the SDK does not know, for example the widgets of the
material_ui and cupertino_ui packages. The SDK matches them with type
checks on T, so the reported names survive minification and
obfuscation.

Built-in widget types are checked first and keep their behavior.
Text labels are opt-in per type with labelFromText, as for the
built-in buttons. A callback that throws is logged and skipped.

Co-Authored-By: Claude <noreply@anthropic.com>
Add material_ui and cupertino_ui as dev dependencies so the user
interaction tests can pump the real widgets. Resolving them needs
Flutter 3.47 or newer.

Co-Authored-By: Claude <noreply@anthropic.com>
@dawid-niedzwiecki
dawid-niedzwiecki marked this pull request as ready for review October 13, 2026 03:05
Resolve conflicts with the 10.0.0 merge: keep the labelFromText
plumbing, take the new map syntax, and reformat the touched files in
the tall style. Taps now start idle spans by default, so the tracing
tests check the streaming span instead of a transaction.

Co-Authored-By: Claude <noreply@anthropic.com>

@buenaflor buenaflor left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks good! Two things I'd change:

  • I'd drop labelFromText. Custom types should just follow the same label rules as the built-in ones (sendDefaultPii for now, maybe mask-aware later with #3770). Once #3772 lands it'd be true everywhere anyway, and it's easier to add a param later than to remove one.

  • I think we can drop addUserInteractionLabel too. Both the Material and material_ui Tooltip build a RawTooltip(semanticsTooltip: message), and RawTooltip is in package:flutter/widgets.dart (since 3.44). So if _getLabel checks RawTooltip instead of Tooltip, tooltip labels just work without any setup. Without that case I don't see a need for a custom label callback right now.

Drop labelFromText and addUserInteractionLabel, as suggested in
review. Added widget types now read Text labels, which matches the
planned default for all types. Tooltip labels come from
RawTooltip.semanticsTooltip, which both the Flutter and the
material_ui Tooltip build, so they work without any setup.

Co-Authored-By: Claude <noreply@anthropic.com>
@dawid-niedzwiecki

dawid-niedzwiecki commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks, @buenaflor! Both make sense! Done in 521df1a:

  • Dropped labelFromText. Added types now follow the built-in label rules, so they read Text labels whenever sendDefaultPii is on.
  • Dropped addUserInteractionLabel. _getLabel reads RawTooltip.semanticsTooltip instead of Tooltip.message, so material_ui tooltips work without setup. For the Flutter Tooltip this means no label when excludeFromSemantics is set or TooltipVisibility hides tooltips, and a label for richMessage tooltips.

I also merged main (10.0.0) and updated the description.

@codecov

codecov Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.81%. Comparing base (7e3e132) to head (f8ef26e).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4069   +/-   ##
=======================================
  Coverage   88.80%   88.81%           
=======================================
  Files         348      349    +1     
  Lines       12627    12637   +10     
=======================================
+ Hits        11213    11223   +10     
  Misses       1414     1414           
Flag Coverage Δ
sentry 88.73% <ø> (ø)
sentry_dio 97.95% <ø> (ø)
sentry_drift 93.63% <ø> (ø)
sentry_file 64.41% <ø> (ø)
sentry_firebase_remote_config 100.00% <ø> (ø)
sentry_flutter 92.68% <100.00%> (+0.01%) ⬆️
sentry_grpc 99.11% <ø> (ø)
sentry_hive 76.37% <ø> (ø)
sentry_isar 73.88% <ø> (ø)
sentry_link 22.04% <ø> (ø)
sentry_logging 97.01% <ø> (ø)
sentry_sqflite 88.88% <ø> (ø)
sentry_supabase 97.48% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@buenaflor buenaflor left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes, looks good to me!

@buenaflor
buenaflor merged commit 17839fe into getsentry:main Oct 30, 2026
133 of 135 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants