[flutter_markdown] Fix mergeInlineChildren when first span is not a TextSpan (#365)
diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md
index 521aee5..d32ccb1 100644
--- a/packages/flutter_markdown/CHANGELOG.md
+++ b/packages/flutter_markdown/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.6.4
+
+ * Fix merging of spans when first span is not a TextSpan
+
## 0.6.3
* Fixed `onTap`, now the changed hyperlinks are reflected even with keeping the same link name unchanged.
diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart
index 1e0da20..7d8e91e 100644
--- a/packages/flutter_markdown/lib/src/builder.dart
+++ b/packages/flutter_markdown/lib/src/builder.dart
@@ -623,7 +623,11 @@
final RichText previous = mergedTexts.removeLast() as RichText;
final TextSpan previousTextSpan = previous.text as TextSpan;
final List<TextSpan> children = previousTextSpan.children != null
- ? List<TextSpan>.from(previousTextSpan.children!)
+ ? previousTextSpan.children!
+ .map((InlineSpan span) => span is! TextSpan
+ ? TextSpan(children: <InlineSpan>[span])
+ : span)
+ .toList()
: <TextSpan>[previousTextSpan];
children.add(child.text as TextSpan);
final TextSpan? mergedSpan = _mergeSimilarTextSpans(children);
diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml
index 2d87412..96b7b08 100644
--- a/packages/flutter_markdown/pubspec.yaml
+++ b/packages/flutter_markdown/pubspec.yaml
@@ -4,7 +4,7 @@
formatted with simple Markdown tags.
repository: https://github.com/flutter/packages/tree/master/packages/flutter_markdown
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+flutter_markdown%22
-version: 0.6.3
+version: 0.6.4
environment:
sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/flutter_markdown/test/custom_syntax_test.dart b/packages/flutter_markdown/test/custom_syntax_test.dart
index c7f5c5b..7fee7ce 100644
--- a/packages/flutter_markdown/test/custom_syntax_test.dart
+++ b/packages/flutter_markdown/test/custom_syntax_test.dart
@@ -7,6 +7,7 @@
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:markdown/markdown.dart' as md;
+
import 'utils.dart';
void main() => defineTests();
@@ -58,7 +59,59 @@
expect(span.recognizer.runtimeType, equals(TapGestureRecognizer));
},
);
+
+ testWidgets(
+ 'WidgetSpan in RichText is handled correctly',
+ (WidgetTester tester) async {
+ await tester.pumpWidget(
+ boilerplate(
+ Markdown(
+ data: 'container is a widget that allows to customize its child',
+ extensionSet: md.ExtensionSet.none,
+ inlineSyntaxes: <md.InlineSyntax>[ContainerSyntax()],
+ builders: <String, MarkdownElementBuilder>{
+ 'container': ContainerBuilder(),
+ },
+ ),
+ ),
+ );
+
+ final RichText textWidget = tester.widget(find.byType(RichText));
+ final TextSpan span =
+ (textWidget.text as TextSpan).children![0] as TextSpan;
+ final WidgetSpan widgetSpan = span.children![0] as WidgetSpan;
+ expect(widgetSpan.child, isInstanceOf<Container>());
+ },
+ );
});
+
+ testWidgets(
+ 'TextSpan and WidgetSpan as children in RichText are handled correctly',
+ (WidgetTester tester) async {
+ await tester.pumpWidget(
+ boilerplate(
+ Markdown(
+ data: 'this test replaces a string with a container',
+ extensionSet: md.ExtensionSet.none,
+ inlineSyntaxes: <md.InlineSyntax>[ContainerSyntax()],
+ builders: <String, MarkdownElementBuilder>{
+ 'container': ContainerBuilder2(),
+ },
+ ),
+ ),
+ );
+
+ final RichText textWidget = tester.widget(find.byType(RichText));
+ final TextSpan textSpan = textWidget.text as TextSpan;
+ final TextSpan start = textSpan.children![0] as TextSpan;
+ expect(start.text, 'this test replaces a string with a ');
+ final TextSpan end = textSpan.children![1] as TextSpan;
+ final TextSpan foo = end.children![0] as TextSpan;
+ expect(foo.text, 'foo');
+ final WidgetSpan widgetSpan = end.children![1] as WidgetSpan;
+ expect(widgetSpan.child, isInstanceOf<Container>());
+ },
+ );
}
class SubscriptSyntax extends md.InlineSyntax {
@@ -126,3 +179,48 @@
);
}
}
+
+class ContainerSyntax extends md.InlineSyntax {
+ ContainerSyntax() : super(_pattern);
+
+ static const String _pattern = 'container';
+
+ @override
+ bool onMatch(md.InlineParser parser, Match match) {
+ parser.addNode(
+ md.Element.text('container', ''),
+ );
+ return true;
+ }
+}
+
+class ContainerBuilder extends MarkdownElementBuilder {
+ @override
+ Widget? visitElementAfter(md.Element element, _) {
+ return RichText(
+ text: TextSpan(
+ children: <InlineSpan>[
+ WidgetSpan(
+ child: Container(),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class ContainerBuilder2 extends MarkdownElementBuilder {
+ @override
+ Widget? visitElementAfter(md.Element element, _) {
+ return RichText(
+ text: TextSpan(
+ children: <InlineSpan>[
+ const TextSpan(text: 'foo'),
+ WidgetSpan(
+ child: Container(),
+ ),
+ ],
+ ),
+ );
+ }
+}