Skip to content

Commit 2668183

Browse files
committed
feat: ✨ Enhance tooltip accessibility using Semantics live region
1 parent 343efe3 commit 2668183

File tree

3 files changed

+85
-69
lines changed

3 files changed

+85
-69
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
- Fixed [#587](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/587) - Fixed ShowcaseView.withWidget container positioning issues on web
55
- Fixed [#588](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/588) & [#593](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/593) - Fixed incorrect ShowcaseView callback scope by adding optional scope parameter
66
- Feature [#600](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/600) - Added dynamic onComplete callback registration with `addOnCompleteCallback` and `removeOnCompleteCallback` methods
7-
- Fixed [#577](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/577) - Resolve issue where long tooltip text was rendered outside the screen bounds
7+
- Fixed [#577](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/577) - Resolve issue where long tooltip text was rendered outside the screen bounds
8+
- Feature [#586](https://github.com/SimformSolutionsPvtLtd/showcaseview/issues/586): Enhance tooltip accessibility using Semantics live region
89

910
## [5.0.1]
1011

lib/src/tooltip/tooltip_wrapper.dart

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -172,48 +172,57 @@ class _ToolTipWrapperState extends State<ToolTipWrapper>
172172
final targetPosition = box.localToGlobal(Offset.zero);
173173
final targetSize = box.size;
174174

175+
// Build the tooltip widget based on whether a custom container is provided.
176+
// Both branches are wrapped with Semantics to improve accessibility by
177+
// marking the tooltip as a live region for screen readers.
175178
final defaultToolTipWidget = widget.container != null
176-
? MouseRegion(
177-
cursor: widget.onTooltipTap == null
178-
? MouseCursor.defer
179-
: SystemMouseCursors.click,
180-
child: GestureDetector(
181-
onTap: widget.onTooltipTap,
182-
child: RepaintBoundary(
183-
child: widget.container ?? const SizedBox.shrink(),
179+
? Semantics(
180+
liveRegion: true,
181+
child: MouseRegion(
182+
cursor: widget.onTooltipTap == null
183+
? MouseCursor.defer
184+
: SystemMouseCursors.click,
185+
child: GestureDetector(
186+
onTap: widget.onTooltipTap,
187+
child: RepaintBoundary(
188+
child: widget.container ?? const SizedBox.shrink(),
189+
),
184190
),
185191
),
186192
)
187-
: MouseRegion(
188-
cursor: widget.onTooltipTap == null
189-
? MouseCursor.defer
190-
: SystemMouseCursors.click,
191-
child: GestureDetector(
192-
onTap: widget.onTooltipTap,
193-
child: RepaintBoundary(
194-
child: Container(
195-
padding: widget.tooltipPadding,
196-
decoration: BoxDecoration(
197-
color: widget.tooltipBackgroundColor,
198-
borderRadius: widget.tooltipBorderRadius ??
199-
const BorderRadius.all(Radius.circular(8)),
200-
),
201-
child: ToolTipContent(
202-
title: widget.title,
203-
description: widget.description,
204-
titleTextAlign: widget.titleTextAlign,
205-
descriptionTextAlign: widget.descriptionTextAlign,
206-
titleAlignment: widget.titleAlignment,
207-
descriptionAlignment: widget.descriptionAlignment,
208-
textColor: widget.textColor,
209-
titleTextStyle: widget.titleTextStyle,
210-
descTextStyle: widget.descTextStyle,
211-
titlePadding: widget.titlePadding,
212-
descriptionPadding: widget.descriptionPadding,
213-
titleTextDirection: widget.titleTextDirection,
214-
descriptionTextDirection: widget.descriptionTextDirection,
215-
tooltipActionConfig: widget.tooltipActionConfig,
216-
tooltipActions: widget.tooltipActions,
193+
: Semantics(
194+
liveRegion: true,
195+
child: MouseRegion(
196+
cursor: widget.onTooltipTap == null
197+
? MouseCursor.defer
198+
: SystemMouseCursors.click,
199+
child: GestureDetector(
200+
onTap: widget.onTooltipTap,
201+
child: RepaintBoundary(
202+
child: Container(
203+
padding: widget.tooltipPadding,
204+
decoration: BoxDecoration(
205+
color: widget.tooltipBackgroundColor,
206+
borderRadius: widget.tooltipBorderRadius ??
207+
const BorderRadius.all(Radius.circular(8)),
208+
),
209+
child: ToolTipContent(
210+
title: widget.title,
211+
description: widget.description,
212+
titleTextAlign: widget.titleTextAlign,
213+
descriptionTextAlign: widget.descriptionTextAlign,
214+
titleAlignment: widget.titleAlignment,
215+
descriptionAlignment: widget.descriptionAlignment,
216+
textColor: widget.textColor,
217+
titleTextStyle: widget.titleTextStyle,
218+
descTextStyle: widget.descTextStyle,
219+
titlePadding: widget.titlePadding,
220+
descriptionPadding: widget.descriptionPadding,
221+
titleTextDirection: widget.titleTextDirection,
222+
descriptionTextDirection: widget.descriptionTextDirection,
223+
tooltipActionConfig: widget.tooltipActionConfig,
224+
tooltipActions: widget.tooltipActions,
225+
),
217226
),
218227
),
219228
),

lib/src/widget/tooltip_action_button_widget.dart

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -51,40 +51,46 @@ class TooltipActionButtonWidget extends StatelessWidget {
5151
Widget build(BuildContext context) {
5252
final theme = Theme.of(context);
5353

54-
return config.button ??
55-
MouseRegion(
56-
cursor: SystemMouseCursors.click,
57-
child: GestureDetector(
58-
onTap: _handleOnTap,
59-
child: Container(
60-
padding: config.padding,
61-
decoration: BoxDecoration(
62-
color: config.backgroundColor ?? theme.primaryColor,
63-
borderRadius: config.borderRadius,
64-
border: config.border,
65-
),
66-
child: Row(
67-
mainAxisSize: MainAxisSize.min,
68-
children: [
69-
if (config.leadIcon case final lead?)
70-
Padding(
71-
padding: lead.padding ?? const EdgeInsets.only(right: 5),
72-
child: lead.icon,
73-
),
74-
Text(
75-
config.name ?? config.type?.actionName ?? '',
76-
style: config.textStyle,
77-
),
78-
if (config.tailIcon case final tail?)
79-
Padding(
80-
padding: tail.padding ?? const EdgeInsets.only(left: 5),
81-
child: tail.icon,
54+
// Semantics widget is used to improve accessibility for screen readers.
55+
return Semantics(
56+
button: true,
57+
liveRegion: true,
58+
child: config.button ??
59+
MouseRegion(
60+
cursor: SystemMouseCursors.click,
61+
child: GestureDetector(
62+
onTap: _handleOnTap,
63+
child: Container(
64+
padding: config.padding,
65+
decoration: BoxDecoration(
66+
color: config.backgroundColor ?? theme.primaryColor,
67+
borderRadius: config.borderRadius,
68+
border: config.border,
69+
),
70+
child: Row(
71+
mainAxisSize: MainAxisSize.min,
72+
children: [
73+
if (config.leadIcon case final lead?)
74+
Padding(
75+
padding:
76+
lead.padding ?? const EdgeInsets.only(right: 5),
77+
child: lead.icon,
78+
),
79+
Text(
80+
config.name ?? config.type?.actionName ?? '',
81+
style: config.textStyle,
8282
),
83-
],
83+
if (config.tailIcon case final tail?)
84+
Padding(
85+
padding: tail.padding ?? const EdgeInsets.only(left: 5),
86+
child: tail.icon,
87+
),
88+
],
89+
),
8490
),
8591
),
8692
),
87-
);
93+
);
8894
}
8995

9096
void _handleOnTap() {

0 commit comments

Comments
 (0)