Skip to content

Commit 4ead1ed

Browse files
xqwztsDaveShuckerow
authored andcommitted
Add GestureRecognizer to children of 'a' Tag (flutter#9)
* Add GestureRecognizer to children of 'a' Tag * Add support links with nested items * Add test for nested elements in links * Add onTap tests
1 parent 21a04bc commit 4ead1ed

File tree

2 files changed

+95
-18
lines changed

2 files changed

+95
-18
lines changed

packages/flutter_markdown/lib/src/builder.dart

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ class MarkdownBuilder implements md.NodeVisitor {
7373
final List<String> _listIndents = <String>[];
7474
final List<_BlockElement> _blocks = <_BlockElement>[];
7575
final List<_InlineElement> _inlines = <_InlineElement>[];
76+
final List<GestureRecognizer> _linkHandlers = <GestureRecognizer>[];
77+
7678

7779
/// Returns widgets that display the given Markdown nodes.
7880
///
@@ -81,6 +83,7 @@ class MarkdownBuilder implements md.NodeVisitor {
8183
_listIndents.clear();
8284
_blocks.clear();
8385
_inlines.clear();
86+
_linkHandlers.clear();
8487

8588
_blocks.add(new _BlockElement(null));
8689
_inlines.add(new _InlineElement());
@@ -98,8 +101,12 @@ class MarkdownBuilder implements md.NodeVisitor {
98101
void visitText(md.Text text) {
99102
if (_blocks.last.tag == null) // Don't allow text directly under the root.
100103
return;
101-
final TextSpan span = _blocks.last.tag == 'pre' ?
102-
delegate.formatText(styleSheet, text.text) : new TextSpan(text: text.text);
104+
final TextSpan span = _blocks.last.tag == 'pre'
105+
? delegate.formatText(styleSheet, text.text)
106+
: new TextSpan(
107+
text: text.text,
108+
recognizer: _linkHandlers.isNotEmpty ? _linkHandlers.last : null,
109+
);
103110
_inlines.last.children.add(span);
104111
}
105112

@@ -114,6 +121,11 @@ class MarkdownBuilder implements md.NodeVisitor {
114121
} else {
115122
_inlines.add(new _InlineElement());
116123
}
124+
125+
if (tag == 'a') {
126+
_linkHandlers.add(delegate.createLink(element.attributes['href']));
127+
}
128+
117129
return true;
118130
}
119131

@@ -179,16 +191,14 @@ class MarkdownBuilder implements md.NodeVisitor {
179191
final _InlineElement parent = _inlines.last;
180192

181193
if (current.children.isNotEmpty) {
182-
GestureRecognizer recognizer;
183-
184-
if (tag == 'a')
185-
recognizer = delegate.createLink(element.attributes['href']);
186-
187194
parent.children.add(new TextSpan(
188195
style: styleSheet.styles[tag],
189-
recognizer: recognizer,
190196
children: current.children,
191197
));
198+
199+
if (tag == 'a') {
200+
_linkHandlers.removeLast();
201+
}
192202
}
193203
}
194204
}

packages/flutter_markdown/test/flutter_markdown_test.dart

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,83 @@ void main() {
8585
]);
8686
});
8787

88-
testWidgets('Links', (WidgetTester tester) async {
89-
await tester
90-
.pumpWidget(_boilerplate(const Markdown(data: '[Link Text](href)')));
91-
92-
final RichText textWidget =
93-
tester.allWidgets.firstWhere((Widget widget) => widget is RichText);
94-
final TextSpan span = textWidget.text;
95-
96-
expect(
97-
span.children[0].recognizer.runtimeType, equals(TapGestureRecognizer));
88+
group('Links', () {
89+
testWidgets('Single link', (WidgetTester tester) async {
90+
String tapResult;
91+
await tester.pumpWidget(_boilerplate(new Markdown(
92+
data: '[Link Text](href)',
93+
onTapLink: (value) => tapResult = value,
94+
)));
95+
96+
final RichText textWidget =
97+
tester.allWidgets.firstWhere((Widget widget) => widget is RichText);
98+
final TextSpan span = textWidget.text;
99+
100+
(span.children[0].children[0].recognizer as TapGestureRecognizer).onTap();
101+
102+
expect(span.children.length, 1);
103+
expect(span.children[0].children.length, 1);
104+
expect(span.children[0].children[0].recognizer.runtimeType,
105+
equals(TapGestureRecognizer));
106+
expect(tapResult, 'href');
107+
});
108+
109+
testWidgets('Link with nested code', (WidgetTester tester) async {
110+
final List<String> tapResults = <String>[];
111+
await tester.pumpWidget(_boilerplate(new Markdown(
112+
data: '[Link `with nested code` Text](href)',
113+
onTapLink: (value) => tapResults.add(value),
114+
)));
115+
116+
final RichText textWidget =
117+
tester.allWidgets.firstWhere((Widget widget) => widget is RichText);
118+
final TextSpan span = textWidget.text;
119+
120+
final List<Type> gestureRecognizerTypes = <Type>[];
121+
span.visitTextSpan((TextSpan textSpan) {
122+
TapGestureRecognizer recognizer = textSpan.recognizer;
123+
gestureRecognizerTypes.add(recognizer.runtimeType);
124+
recognizer.onTap();
125+
return true;
126+
});
127+
128+
expect(span.children.length, 1);
129+
expect(span.children[0].children.length, 3);
130+
expect(gestureRecognizerTypes, everyElement(TapGestureRecognizer));
131+
expect(tapResults.length, 3);
132+
expect(tapResults, everyElement('href'));
133+
});
134+
135+
testWidgets('Multiple links', (WidgetTester tester) async {
136+
final List<String> tapResults = <String>[];
137+
138+
await tester.pumpWidget(_boilerplate(new Markdown(
139+
data: '[First Link](firstHref) and [Second Link](secondHref)',
140+
onTapLink: (value) => tapResults.add(value),
141+
)));
142+
143+
final RichText textWidget =
144+
tester.allWidgets.firstWhere((Widget widget) => widget is RichText);
145+
final TextSpan span = textWidget.text;
146+
147+
final List<Type> gestureRecognizerTypes = <Type>[];
148+
span.visitTextSpan((TextSpan textSpan) {
149+
TapGestureRecognizer recognizer = textSpan.recognizer;
150+
gestureRecognizerTypes.add(recognizer.runtimeType);
151+
recognizer?.onTap();
152+
return true;
153+
});
154+
155+
156+
expect(span.children.length, 3);
157+
expect(span.children[0].children.length, 1);
158+
expect(span.children[1].children, null);
159+
expect(span.children[2].children.length, 1);
160+
161+
expect(gestureRecognizerTypes,
162+
orderedEquals([TapGestureRecognizer, Null, TapGestureRecognizer]));
163+
expect(tapResults, orderedEquals(['firstHref', 'secondHref']));
164+
});
98165
});
99166

100167
testWidgets('HTML tag ignored ', (WidgetTester tester) async {

0 commit comments

Comments
 (0)