Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
wip
  • Loading branch information
pedromassango committed Aug 26, 2025
commit 5f2cb623478ff4fafa21475a9c05d5f602fde2a3
Original file line number Diff line number Diff line change
Expand Up @@ -212,54 +212,41 @@ void main() {
);
});

testWidgets(
'excludeSemantics: true ensures clean link semantics without conflicts',
(WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: Column(
children: <Widget>[
WebLinkDelegate(
TestLinkInfo(
uri: Uri.parse('https://dart.dev/xyz'),
target: LinkTarget.blank,
builder: (BuildContext context, FollowLink? followLink) {
return ElevatedButton(
onPressed: followLink,
child: const Text('First Button'),
);
},
testWidgets('MergeSemantics is always present to avoid duplicate nodes', (
WidgetTester tester,
) async {
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: Column(
children: <Widget>[
WebLinkDelegate(
TestLinkInfo(
uri: Uri.parse('https://dart.dev/xyz'),
target: LinkTarget.blank,
builder: (BuildContext context, FollowLink? followLink) {
return ElevatedButton(
onPressed: followLink,
child: const Text('First Button'),
);
},
),
),
),
],
],
),
),
),
));
);

await tester.pumpAndSettle();
final SemanticsHandle handle = tester.ensureSemantics();

final Finder buttonFinder = find.text('First Button');
final Finder buttonFinder = find.byType(ElevatedButton);
expect(buttonFinder, findsOneWidget);

final SemanticsNode firstSemantics = tester.getSemantics(buttonFinder);
final SemanticsData firstData = firstSemantics.getSemanticsData();

expect(
firstData.hasFlag(SemanticsFlag.isLink),
isTrue,
reason: 'Button should be treated as link with excludeSemantics: true',
);

expect(
firstData.hasFlag(SemanticsFlag.isButton),
isFalse,
reason:
'semantics should be excluded to prevent TAB navigation conflicts',
);

expect(firstData.linkUrl?.toString(), equals('https://dart.dev/xyz'));
handle.dispose();
final Element buttonElement = tester.element(buttonFinder);
final MergeSemantics? parentWidget =
buttonElement.findAncestorWidgetOfExactType<MergeSemantics>();
expect(parentWidget, isNotNull);
});
});

Expand Down
17 changes: 9 additions & 8 deletions packages/url_launcher/url_launcher_web/lib/src/link.dart
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,15 @@ class WebLinkDelegateState extends State<WebLinkDelegate> {
}

Widget _buildChild(BuildContext context) {
return Semantics(
link: true,
excludeSemantics: true,
identifier: _semanticsIdentifier,
linkUrl: widget.link.uri,
child: widget.link.builder(
context,
widget.link.isDisabled ? null : _followLink,
return MergeSemantics(
child: Semantics(
link: true,
identifier: _semanticsIdentifier,
linkUrl: widget.link.uri,
child: widget.link.builder(
context,
widget.link.isDisabled ? null : _followLink,
),
),
);
}
Expand Down