From d466fab271ce282bd1fcd9c9a959bd2959117c2d Mon Sep 17 00:00:00 2001 From: patrick Date: Tue, 27 Feb 2024 10:07:52 +0100 Subject: [PATCH 01/16] Enable RichText merging --- .../flutter_markdown/lib/src/builder.dart | 88 ++++++++++++------- 1 file changed, 54 insertions(+), 34 deletions(-) diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 91371dd1c6e..7810bcadc43 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -712,47 +712,67 @@ class MarkdownBuilder implements md.NodeVisitor { List children, TextAlign? textAlign, ) { + // List of merged text spans and widgets final List mergedTexts = []; + for (final Widget child in children) { - if (mergedTexts.isNotEmpty && mergedTexts.last is Text && child is Text) { - final Text previous = mergedTexts.removeLast() as Text; - final TextSpan previousTextSpan = previous.textSpan! as TextSpan; - final List children = previousTextSpan.children != null - ? previousTextSpan.children! - .map((InlineSpan span) => span is! TextSpan - ? TextSpan(children: [span]) - : span) - .toList() - : [previousTextSpan]; - children.add(child.textSpan! as TextSpan); - final TextSpan? mergedSpan = _mergeSimilarTextSpans(children); - mergedTexts.add(_buildRichText( - mergedSpan, - textAlign: textAlign, - )); - } else if (mergedTexts.isNotEmpty && - mergedTexts.last is SelectableText && - child is SelectableText) { - final SelectableText previous = - mergedTexts.removeLast() as SelectableText; - final TextSpan previousTextSpan = previous.textSpan!; - final List children = previousTextSpan.children != null - ? List.from(previousTextSpan.children!) - : [previousTextSpan]; - if (child.textSpan != null) { - children.add(child.textSpan!); + if (mergedTexts.isEmpty) { + mergedTexts.add(child); + continue; + } + + final bool lastIsSelectableText = mergedTexts.last is SelectableText; + final bool lastText = mergedTexts.last is Text; + final bool lastRichText = mergedTexts.last is RichText; + + final List spans = []; + + if (lastIsSelectableText) { + final SelectableText last = mergedTexts.removeLast() as SelectableText; + final TextSpan span = last.textSpan!; + + if (span.children != null) { + spans.addAll(span.children!); + } else { + spans.add(span); } - final TextSpan? mergedSpan = _mergeSimilarTextSpans(children); - mergedTexts.add( - _buildRichText( - mergedSpan, - textAlign: textAlign, - ), - ); + } else if (lastText) { + final Text last = mergedTexts.removeLast() as Text; + final InlineSpan span = last.textSpan!; + spans.add(span); + } else if (lastRichText) { + final RichText last = mergedTexts.removeLast() as RichText; + final InlineSpan span = last.text; + spans.add(span); + } + + final bool childIsText = child is Text; + final bool childIsSelectableText = child is SelectableText; + final bool childIsRichText = child is RichText; + + if (childIsText) { + spans.add(child.textSpan!); + } else if (childIsSelectableText) { + final TextSpan span = child.textSpan!; + + if (span.children != null) { + spans.addAll(span.children!); + } else { + spans.add(span); + } + } else if (childIsRichText) { + spans.add(child.text); + } + + if (spans.isNotEmpty) { + mergedTexts.add(RichText( + text: TextSpan(children: spans), + )); } else { mergedTexts.add(child); } } + return mergedTexts; } From 3ad85bfd077228eafb7a290d20a11d5ab3c510c4 Mon Sep 17 00:00:00 2001 From: patrick Date: Tue, 27 Feb 2024 10:49:00 +0100 Subject: [PATCH 02/16] Make text selectable --- .../flutter_markdown/lib/src/builder.dart | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 7810bcadc43..d60a2ed77fc 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -765,9 +765,24 @@ class MarkdownBuilder implements md.NodeVisitor { } if (spans.isNotEmpty) { - mergedTexts.add(RichText( - text: TextSpan(children: spans), - )); + Widget merged; + + if (selectable) { + merged = SelectableText.rich( + TextSpan(children: spans), + textScaler: styleSheet.textScaler, + textAlign: textAlign ?? TextAlign.start, + onTap: onTapText, + ); + } else { + merged = Text.rich( + TextSpan(children: spans), + textScaler: styleSheet.textScaler, + textAlign: textAlign ?? TextAlign.start, + ); + } + + mergedTexts.add(merged); } else { mergedTexts.add(child); } From 9ed8601a008914e12e847790c00271111bdf7e05 Mon Sep 17 00:00:00 2001 From: patrick Date: Wed, 28 Feb 2024 14:12:07 +0100 Subject: [PATCH 03/16] Added _getInlineSpans function --- .../flutter_markdown/lib/src/builder.dart | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index d60a2ed77fc..7ac0243c7d1 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -707,6 +707,23 @@ class MarkdownBuilder implements md.NodeVisitor { } } + /// Extracts all spans from an inline element and merges them into a single list + List _getInlineSpans(InlineSpan span) { + final List spans = []; + + if (span is TextSpan && span.children != null) { + if (span.children != null) { + spans.addAll(span.children!); + } else { + spans.add(span); + } + } else { + spans.add(span); + } + + return spans; + } + /// Merges adjacent [TextSpan] children List _mergeInlineChildren( List children, @@ -730,20 +747,15 @@ class MarkdownBuilder implements md.NodeVisitor { if (lastIsSelectableText) { final SelectableText last = mergedTexts.removeLast() as SelectableText; final TextSpan span = last.textSpan!; - - if (span.children != null) { - spans.addAll(span.children!); - } else { - spans.add(span); - } + spans.addAll(_getInlineSpans(span)); } else if (lastText) { final Text last = mergedTexts.removeLast() as Text; final InlineSpan span = last.textSpan!; - spans.add(span); + spans.addAll(_getInlineSpans(span)); } else if (lastRichText) { final RichText last = mergedTexts.removeLast() as RichText; final InlineSpan span = last.text; - spans.add(span); + spans.addAll(_getInlineSpans(span)); } final bool childIsText = child is Text; @@ -751,17 +763,14 @@ class MarkdownBuilder implements md.NodeVisitor { final bool childIsRichText = child is RichText; if (childIsText) { - spans.add(child.textSpan!); + final InlineSpan span = child.textSpan!; + spans.addAll(_getInlineSpans(span)); } else if (childIsSelectableText) { final TextSpan span = child.textSpan!; - - if (span.children != null) { - spans.addAll(span.children!); - } else { - spans.add(span); - } + spans.addAll(_getInlineSpans(span)); } else if (childIsRichText) { - spans.add(child.text); + final InlineSpan span = child.text; + spans.addAll(_getInlineSpans(span)); } if (spans.isNotEmpty) { From 6c9fe97c6b06599602df1203e276b2fcb1e5b406 Mon Sep 17 00:00:00 2001 From: patrick Date: Wed, 28 Feb 2024 14:19:03 +0100 Subject: [PATCH 04/16] Fix 'WidgetSpan in Text.rich is handled correctly' test --- packages/flutter_markdown/test/custom_syntax_test.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/flutter_markdown/test/custom_syntax_test.dart b/packages/flutter_markdown/test/custom_syntax_test.dart index 1db01e3c0f9..622289cb499 100644 --- a/packages/flutter_markdown/test/custom_syntax_test.dart +++ b/packages/flutter_markdown/test/custom_syntax_test.dart @@ -77,9 +77,8 @@ void defineTests() { ); final Text textWidget = tester.widget(find.byType(Text)); - final TextSpan span = - (textWidget.textSpan! as TextSpan).children![0] as TextSpan; - final WidgetSpan widgetSpan = span.children![0] as WidgetSpan; + final TextSpan textSpan = textWidget.textSpan! as TextSpan; + final WidgetSpan widgetSpan = textSpan.children![0] as WidgetSpan; expect(widgetSpan.child, isInstanceOf()); }, ); From 6db3f83ba47a1fe708e0b80685ffbd9887c7926d Mon Sep 17 00:00:00 2001 From: patrick Date: Wed, 28 Feb 2024 17:04:09 +0100 Subject: [PATCH 05/16] Modify _mergeSimilarTextSpans to handle a list of InlineSpans --- .../flutter_markdown/lib/src/builder.dart | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 7ac0243c7d1..62c208a8a9e 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -742,7 +742,7 @@ class MarkdownBuilder implements md.NodeVisitor { final bool lastText = mergedTexts.last is Text; final bool lastRichText = mergedTexts.last is RichText; - final List spans = []; + List spans = []; if (lastIsSelectableText) { final SelectableText last = mergedTexts.removeLast() as SelectableText; @@ -776,6 +776,15 @@ class MarkdownBuilder implements md.NodeVisitor { if (spans.isNotEmpty) { Widget merged; + spans = _mergeSimilarTextSpans(spans); + + InlineSpan child; + if (spans.length == 1) { + child = spans.first; + } else { + child = TextSpan(children: spans); + } + if (selectable) { merged = SelectableText.rich( TextSpan(children: spans), @@ -785,7 +794,7 @@ class MarkdownBuilder implements md.NodeVisitor { ); } else { merged = Text.rich( - TextSpan(children: spans), + child, textScaler: styleSheet.textScaler, textAlign: textAlign ?? TextAlign.start, ); @@ -871,19 +880,30 @@ class MarkdownBuilder implements md.NodeVisitor { } /// Combine text spans with equivalent properties into a single span. - TextSpan? _mergeSimilarTextSpans(List? textSpans) { - if (textSpans == null || textSpans.length < 2) { - return TextSpan(children: textSpans); + List _mergeSimilarTextSpans(List textSpans) { + if (textSpans.length < 2) { + return textSpans; } - final List mergedSpans = [textSpans.first]; + final List mergedSpans = []; for (int index = 1; index < textSpans.length; index++) { - final TextSpan nextChild = textSpans[index]; - if (nextChild.recognizer == mergedSpans.last.recognizer && - nextChild.semanticsLabel == mergedSpans.last.semanticsLabel && - nextChild.style == mergedSpans.last.style) { - final TextSpan previous = mergedSpans.removeLast(); + final InlineSpan previous = + mergedSpans.isEmpty ? textSpans.first : mergedSpans.removeLast(); + final InlineSpan nextChild = textSpans[index]; + + final bool previousIsTextSpan = previous is TextSpan; + final bool nextIsTextSpan = nextChild is TextSpan; + if (!previousIsTextSpan || !nextIsTextSpan) { + mergedSpans.addAll([previous, nextChild]); + continue; + } + + final bool matchStyle = nextChild.recognizer == previous.recognizer && + nextChild.semanticsLabel == previous.semanticsLabel && + nextChild.style == previous.style; + + if (matchStyle) { mergedSpans.add(TextSpan( text: previous.toPlainText() + nextChild.toPlainText(), recognizer: previous.recognizer, @@ -891,15 +911,13 @@ class MarkdownBuilder implements md.NodeVisitor { style: previous.style, )); } else { - mergedSpans.add(nextChild); + mergedSpans.addAll([previous, nextChild]); } } // When the mergered spans compress into a single TextSpan return just that // TextSpan, otherwise bundle the set of TextSpans under a single parent. - return mergedSpans.length == 1 - ? mergedSpans.first - : TextSpan(children: mergedSpans); + return mergedSpans; } Widget _buildRichText(TextSpan? text, {TextAlign? textAlign, String? key}) { From 716f1e70cfe9c91b3714b516c97dfb1ec31eb14b Mon Sep 17 00:00:00 2001 From: patrick Date: Wed, 28 Feb 2024 17:10:04 +0100 Subject: [PATCH 06/16] Fix test 'TextSpan and WidgetSpan as children in Text.rich are handled correctly' --- packages/flutter_markdown/test/custom_syntax_test.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/flutter_markdown/test/custom_syntax_test.dart b/packages/flutter_markdown/test/custom_syntax_test.dart index 622289cb499..2bee4ebbf0b 100644 --- a/packages/flutter_markdown/test/custom_syntax_test.dart +++ b/packages/flutter_markdown/test/custom_syntax_test.dart @@ -132,10 +132,9 @@ void defineTests() { final TextSpan textSpan = textWidget.textSpan! 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; + final TextSpan foo = textSpan.children![1] as TextSpan; expect(foo.text, 'foo'); - final WidgetSpan widgetSpan = end.children![1] as WidgetSpan; + final WidgetSpan widgetSpan = textSpan.children![2] as WidgetSpan; expect(widgetSpan.child, isInstanceOf()); }, ); From 82f6e6330ccf3d8e0e3032b195fbc5f31beffcde Mon Sep 17 00:00:00 2001 From: patrick Date: Wed, 28 Feb 2024 17:44:09 +0100 Subject: [PATCH 07/16] Fix issue where certain children were not added correctly --- .../flutter_markdown/lib/src/builder.dart | 52 ++++++++++--------- .../flutter_markdown/test/image_test.dart | 2 + 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 62c208a8a9e..e8f4b30da56 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -733,51 +733,55 @@ class MarkdownBuilder implements md.NodeVisitor { final List mergedTexts = []; for (final Widget child in children) { + // If the list is empty, add the current widget to the list if (mergedTexts.isEmpty) { mergedTexts.add(child); continue; } - final bool lastIsSelectableText = mergedTexts.last is SelectableText; - final bool lastText = mergedTexts.last is Text; - final bool lastRichText = mergedTexts.last is RichText; + // Remove last widget from the list to merge it with the current widget + final Widget last = mergedTexts.removeLast(); + // Extract the text spans from the last widget(s) List spans = []; - if (lastIsSelectableText) { - final SelectableText last = mergedTexts.removeLast() as SelectableText; + // Extract the text spans from the last widget + if (last is SelectableText) { final TextSpan span = last.textSpan!; spans.addAll(_getInlineSpans(span)); - } else if (lastText) { - final Text last = mergedTexts.removeLast() as Text; + } else if (last is Text) { final InlineSpan span = last.textSpan!; spans.addAll(_getInlineSpans(span)); - } else if (lastRichText) { - final RichText last = mergedTexts.removeLast() as RichText; + } else if (last is RichText) { final InlineSpan span = last.text; spans.addAll(_getInlineSpans(span)); + } else { + // If the last widget is not a text widget, add it back to the list + mergedTexts.addAll([last, child]); + continue; } - final bool childIsText = child is Text; - final bool childIsSelectableText = child is SelectableText; - final bool childIsRichText = child is RichText; - - if (childIsText) { + // Extract the text spans from the current widget + if (child is Text) { final InlineSpan span = child.textSpan!; spans.addAll(_getInlineSpans(span)); - } else if (childIsSelectableText) { + } else if (child is SelectableText) { final TextSpan span = child.textSpan!; spans.addAll(_getInlineSpans(span)); - } else if (childIsRichText) { + } else if (child is RichText) { final InlineSpan span = child.text; spans.addAll(_getInlineSpans(span)); + } else { + // If the current widget is not a text widget, add it back to the list + mergedTexts.addAll([last, child]); + continue; } if (spans.isNotEmpty) { - Widget merged; - + // Merge similar text spans spans = _mergeSimilarTextSpans(spans); + // Create a new text widget with the merged text spans InlineSpan child; if (spans.length == 1) { child = spans.first; @@ -785,23 +789,23 @@ class MarkdownBuilder implements md.NodeVisitor { child = TextSpan(children: spans); } + // Add the new text widget to the list if (selectable) { - merged = SelectableText.rich( + mergedTexts.add(SelectableText.rich( TextSpan(children: spans), textScaler: styleSheet.textScaler, textAlign: textAlign ?? TextAlign.start, onTap: onTapText, - ); + )); } else { - merged = Text.rich( + mergedTexts.add(Text.rich( child, textScaler: styleSheet.textScaler, textAlign: textAlign ?? TextAlign.start, - ); + )); } - - mergedTexts.add(merged); } else { + // If no text spans were found, add the current widget to the list mergedTexts.add(child); } } diff --git a/packages/flutter_markdown/test/image_test.dart b/packages/flutter_markdown/test/image_test.dart index 25b0788a42c..accd5f03946 100644 --- a/packages/flutter_markdown/test/image_test.dart +++ b/packages/flutter_markdown/test/image_test.dart @@ -257,6 +257,8 @@ void defineTests() { ), ); + print('######### '); + final GestureDetector detector = tester.widget(find.byType(GestureDetector)); detector.onTap!(); From ff30c874001f3de40c046860ba133b54d69f18c0 Mon Sep 17 00:00:00 2001 From: patrick Date: Wed, 28 Feb 2024 17:55:11 +0100 Subject: [PATCH 08/16] Update comments --- packages/flutter_markdown/lib/src/builder.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index e8f4b30da56..7d59fce0650 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -742,7 +742,7 @@ class MarkdownBuilder implements md.NodeVisitor { // Remove last widget from the list to merge it with the current widget final Widget last = mergedTexts.removeLast(); - // Extract the text spans from the last widget(s) + // Extracted spans from the last and the current widget List spans = []; // Extract the text spans from the last widget @@ -756,7 +756,8 @@ class MarkdownBuilder implements md.NodeVisitor { final InlineSpan span = last.text; spans.addAll(_getInlineSpans(span)); } else { - // If the last widget is not a text widget, add it back to the list + // If the last widget is not a text widget, + // add both the last and the current widget to the list mergedTexts.addAll([last, child]); continue; } @@ -772,7 +773,8 @@ class MarkdownBuilder implements md.NodeVisitor { final InlineSpan span = child.text; spans.addAll(_getInlineSpans(span)); } else { - // If the current widget is not a text widget, add it back to the list + // If the current widget is not a text widget, + // add both the last and the current widget to the list mergedTexts.addAll([last, child]); continue; } From 4771d843501dd9434865b5b9bf9662cd1bbdb903 Mon Sep 17 00:00:00 2001 From: patrick Date: Thu, 29 Feb 2024 12:12:23 +0100 Subject: [PATCH 09/16] Added inline widget tests --- .../test/inline_widget_test.dart | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 packages/flutter_markdown/test/inline_widget_test.dart diff --git a/packages/flutter_markdown/test/inline_widget_test.dart b/packages/flutter_markdown/test/inline_widget_test.dart new file mode 100644 index 00000000000..057d0f28052 --- /dev/null +++ b/packages/flutter_markdown/test/inline_widget_test.dart @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +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(); + +void defineTests() { + group('InlineWidget', () { + testWidgets( + 'Test inline widget', + (WidgetTester tester) async { + await tester.pumpWidget( + boilerplate( + MarkdownBody( + data: 'Hello, foo bar', + builders: { + 'sub': SubscriptBuilder(), + }, + extensionSet: md.ExtensionSet( + [], + [SubscriptSyntax()], + ), + ), + ), + ); + + final Text textWidget = tester.firstWidget(find.byType(Text)); + final TextSpan span = textWidget.textSpan! as TextSpan; + + final TextSpan part1 = span.children![0] as TextSpan; + expect(part1.toPlainText(), 'Hello, '); + + final WidgetSpan part2 = span.children![1] as WidgetSpan; + expect(part2.alignment, PlaceholderAlignment.middle); + expect(part2.child, isA()); + expect((part2.child as Text).data, 'foo'); + + final TextSpan part3 = span.children![2] as TextSpan; + expect(part3.toPlainText(), ' bar'); + }, + ); + }); +} + +class SubscriptBuilder extends MarkdownElementBuilder { + @override + Widget visitElementAfterWithContext( + BuildContext context, + md.Element element, + TextStyle? preferredStyle, + TextStyle? parentStyle, + ) { + // This pull request will add support for WidgetSpan in the FlutterMarkdown package + return Text.rich(WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Text(element.textContent), + )); + } +} + +class SubscriptSyntax extends md.InlineSyntax { + SubscriptSyntax() : super(_pattern); + + static const String _pattern = r'(foo)'; + + @override + bool onMatch(md.InlineParser parser, Match match) { + parser.addNode(md.Element.text('sub', match[1]!)); + return true; + } +} From 60e981bb3b3c6b5fa02711ff68abe014274dc3be Mon Sep 17 00:00:00 2001 From: patrick Date: Thu, 29 Feb 2024 12:17:02 +0100 Subject: [PATCH 10/16] Update CHANGELOG.md --- packages/flutter_markdown/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md index 7b19e96b64a..dc25b29d7d1 100644 --- a/packages/flutter_markdown/CHANGELOG.md +++ b/packages/flutter_markdown/CHANGELOG.md @@ -1,3 +1,7 @@ +## NEXT + +* Fixes support for `WidgetSpan` in `Text.rich` elements inside `MarkdownElementBuilder`. + ## 0.6.20+1 * Updates minimum supported SDK version to Flutter 3.19. From 0aef52c17d98af26e0389b04539416484936c611 Mon Sep 17 00:00:00 2001 From: patrick Date: Sat, 2 Mar 2024 11:36:15 +0100 Subject: [PATCH 11/16] Fix missing type annotation --- packages/flutter_markdown/lib/src/builder.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 7d59fce0650..04adfed8b0b 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -901,7 +901,7 @@ class MarkdownBuilder implements md.NodeVisitor { final bool previousIsTextSpan = previous is TextSpan; final bool nextIsTextSpan = nextChild is TextSpan; if (!previousIsTextSpan || !nextIsTextSpan) { - mergedSpans.addAll([previous, nextChild]); + mergedSpans.addAll([previous, nextChild]); continue; } @@ -917,7 +917,7 @@ class MarkdownBuilder implements md.NodeVisitor { style: previous.style, )); } else { - mergedSpans.addAll([previous, nextChild]); + mergedSpans.addAll([previous, nextChild]); } } From 8e438bfaf25392299aa96e1508d861491abbce74 Mon Sep 17 00:00:00 2001 From: patrick Date: Sat, 2 Mar 2024 11:36:57 +0100 Subject: [PATCH 12/16] Remove print --- packages/flutter_markdown/test/image_test.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/flutter_markdown/test/image_test.dart b/packages/flutter_markdown/test/image_test.dart index accd5f03946..25b0788a42c 100644 --- a/packages/flutter_markdown/test/image_test.dart +++ b/packages/flutter_markdown/test/image_test.dart @@ -257,8 +257,6 @@ void defineTests() { ), ); - print('######### '); - final GestureDetector detector = tester.widget(find.byType(GestureDetector)); detector.onTap!(); From 8525899215a58bef8bda39c281ffcf90548768d1 Mon Sep 17 00:00:00 2001 From: patrick Date: Sat, 2 Mar 2024 12:03:23 +0100 Subject: [PATCH 13/16] Update package version --- packages/flutter_markdown/CHANGELOG.md | 2 +- packages/flutter_markdown/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md index dc25b29d7d1..e0fe8c99c3f 100644 --- a/packages/flutter_markdown/CHANGELOG.md +++ b/packages/flutter_markdown/CHANGELOG.md @@ -1,4 +1,4 @@ -## NEXT +## 0.6.21 * Fixes support for `WidgetSpan` in `Text.rich` elements inside `MarkdownElementBuilder`. diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml index c5b8d4706f1..9811a80d213 100644 --- a/packages/flutter_markdown/pubspec.yaml +++ b/packages/flutter_markdown/pubspec.yaml @@ -4,7 +4,7 @@ description: A Markdown renderer for Flutter. Create rich text output, formatted with simple Markdown tags. repository: https://github.com/flutter/packages/tree/main/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.20+1 +version: 0.6.21 environment: sdk: ^3.3.0 From 6dfd49d9a99687fa8b6fd5896b26450f6e8925e1 Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 4 Mar 2024 09:52:41 +0100 Subject: [PATCH 14/16] Ensure that sub spans match parent style --- .../flutter_markdown/lib/src/builder.dart | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 04adfed8b0b..ddfbf874dd6 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -708,18 +708,31 @@ class MarkdownBuilder implements md.NodeVisitor { } /// Extracts all spans from an inline element and merges them into a single list - List _getInlineSpans(InlineSpan span) { - final List spans = []; + Iterable _getInlineSpans(InlineSpan span) { + // If the span is not a TextSpan, return it as a single span + if (span is! TextSpan) { + return [span]; + } + + // If the span has no children, return it as a single span + if (span.children == null) { + return [span]; + } - if (span is TextSpan && span.children != null) { - if (span.children != null) { - spans.addAll(span.children!); + // Merge the style of the parent with the style of the children + final Iterable spans = + span.children!.map((InlineSpan childSpan) { + if (childSpan is TextSpan) { + return TextSpan( + text: childSpan.text, + recognizer: childSpan.recognizer, + semanticsLabel: childSpan.semanticsLabel, + style: childSpan.style?.merge(span.style), + ); } else { - spans.add(span); + return childSpan; } - } else { - spans.add(span); - } + }); return spans; } From ffae1885a495b169852cfd8373ec58d06d7441f5 Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 4 Mar 2024 09:53:58 +0100 Subject: [PATCH 15/16] Simplified _getInlineSpans function --- packages/flutter_markdown/lib/src/builder.dart | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index ddfbf874dd6..6188099429f 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -709,13 +709,8 @@ class MarkdownBuilder implements md.NodeVisitor { /// Extracts all spans from an inline element and merges them into a single list Iterable _getInlineSpans(InlineSpan span) { - // If the span is not a TextSpan, return it as a single span - if (span is! TextSpan) { - return [span]; - } - - // If the span has no children, return it as a single span - if (span.children == null) { + // If the span is not a TextSpan or it has no children, return the span + if (span is! TextSpan || span.children == null) { return [span]; } From 041bc041057b197b3eb948e5ee460b872b54744c Mon Sep 17 00:00:00 2001 From: patrick Date: Tue, 5 Mar 2024 10:08:30 +0100 Subject: [PATCH 16/16] Remove comment --- packages/flutter_markdown/test/inline_widget_test.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/flutter_markdown/test/inline_widget_test.dart b/packages/flutter_markdown/test/inline_widget_test.dart index 057d0f28052..d00102cc218 100644 --- a/packages/flutter_markdown/test/inline_widget_test.dart +++ b/packages/flutter_markdown/test/inline_widget_test.dart @@ -58,7 +58,6 @@ class SubscriptBuilder extends MarkdownElementBuilder { TextStyle? preferredStyle, TextStyle? parentStyle, ) { - // This pull request will add support for WidgetSpan in the FlutterMarkdown package return Text.rich(WidgetSpan( alignment: PlaceholderAlignment.middle, child: Text(element.textContent),