Skip to content

[flutter_markdown] Use Text.rich to replace RichText in Flutter Markdown #6062

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 4 additions & 0 deletions packages/flutter_markdown/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.6.19

* Replaces `RichText` with `Text.rich` so the widget can work with `SelectionArea` when `selectable` is set to false.

## 0.6.18+3

* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0.
Expand Down
24 changes: 11 additions & 13 deletions packages/flutter_markdown/lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ class _TableElement {
/// A collection of widgets that should be placed adjacent to (inline with)
/// other inline elements in the same parent block.
///
/// Inline elements can be textual (a/em/strong) represented by [RichText]
/// Inline elements can be textual (a/em/strong) represented by [Text.rich]
/// widgets or images (img) represented by [Image.network] widgets.
///
/// Inline elements can be nested within other inline elements, inheriting their
/// parent's style along with the style of the block they are in.
///
/// When laying out inline widgets, first, any adjacent RichText widgets are
/// When laying out inline widgets, first, any adjacent Text.rich widgets are
/// merged, then, all inline widgets are enclosed in a parent [Wrap] widget.
class _InlineElement {
_InlineElement(this.tag, {this.style});
Expand Down Expand Up @@ -517,8 +517,8 @@ class MarkdownBuilder implements md.NodeVisitor {
} else if (tag == 'sup') {
final Widget c = current.children.last;
TextSpan? textSpan;
if (c is RichText && c.text is TextSpan) {
textSpan = c.text as TextSpan;
if (c is Text && c.textSpan is TextSpan) {
textSpan = c.textSpan! as TextSpan;
} else if (c is SelectableText && c.textSpan is TextSpan) {
textSpan = c.textSpan;
}
Expand Down Expand Up @@ -717,19 +717,17 @@ class MarkdownBuilder implements md.NodeVisitor {
) {
final List<Widget> mergedTexts = <Widget>[];
for (final Widget child in children) {
if (mergedTexts.isNotEmpty &&
mergedTexts.last is RichText &&
child is RichText) {
final RichText previous = mergedTexts.removeLast() as RichText;
final TextSpan previousTextSpan = previous.text as TextSpan;
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<TextSpan> children = previousTextSpan.children != null
? previousTextSpan.children!
.map((InlineSpan span) => span is! TextSpan
? TextSpan(children: <InlineSpan>[span])
: span)
.toList()
: <TextSpan>[previousTextSpan];
children.add(child.text as TextSpan);
children.add(child.textSpan! as TextSpan);
final TextSpan? mergedSpan = _mergeSimilarTextSpans(children);
mergedTexts.add(_buildRichText(
mergedSpan,
Expand Down Expand Up @@ -876,10 +874,10 @@ class MarkdownBuilder implements md.NodeVisitor {
key: k,
);
} else {
return RichText(
text: text!,
return Text.rich(
text!,
// ignore: deprecated_member_use
textScaleFactor: styleSheet.textScaleFactor!,
textScaleFactor: styleSheet.textScaleFactor,
textAlign: textAlign ?? TextAlign.start,
key: k,
);
Expand Down
2 changes: 1 addition & 1 deletion packages/flutter_markdown/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.18+3
version: 0.6.19

environment:
sdk: ">=3.0.0 <4.0.0"
Expand Down
2 changes: 2 additions & 0 deletions packages/flutter_markdown/test/all.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'line_break_test.dart' as line_break_test;
import 'link_test.dart' as link_test;
import 'list_test.dart' as list_test;
import 'scrollable_test.dart' as scrollable_test;
import 'selection_area_compatibility_test.dart' as selection_area_test;
import 'style_sheet_test.dart' as style_sheet_test;
import 'table_test.dart' as table_test;
import 'text_alignment_test.dart' as text_alignment_test;
Expand All @@ -34,6 +35,7 @@ void main() {
link_test.defineTests();
list_test.defineTests();
scrollable_test.defineTests();
selection_area_test.defineTests();
style_sheet_test.defineTests();
table_test.defineTests();
text_test.defineTests();
Expand Down
4 changes: 2 additions & 2 deletions packages/flutter_markdown/test/blockquote_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ void defineTests() {
final DecoratedBox blockQuoteContainer = tester.widget(
find.byType(DecoratedBox),
);
final RichText qouteText = tester.widget(find.byType(RichText));
final Text qouteText = tester.widget(find.byType(Text));
final List<TextSpan> styledTextParts =
(qouteText.text as TextSpan).children!.cast<TextSpan>();
(qouteText.textSpan! as TextSpan).children!.cast<TextSpan>();

expectTextStrings(
widgets,
Expand Down
42 changes: 20 additions & 22 deletions packages/flutter_markdown/test/custom_syntax_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,17 @@ void defineTests() {
),
);

final RichText textWidget = tester.widget(find.byType(RichText));
final Text textWidget = tester.widget(find.byType(Text));
final TextSpan span =
(textWidget.text as TextSpan).children![1] as TextSpan;
(textWidget.textSpan! as TextSpan).children![1] as TextSpan;

expect(span.children, null);
expect(span.recognizer.runtimeType, equals(TapGestureRecognizer));
},
);

testWidgets(
'WidgetSpan in RichText is handled correctly',
'WidgetSpan in Text.rich is handled correctly',
(WidgetTester tester) async {
await tester.pumpWidget(
boilerplate(
Expand All @@ -76,9 +76,9 @@ void defineTests() {
),
);

final RichText textWidget = tester.widget(find.byType(RichText));
final Text textWidget = tester.widget(find.byType(Text));
final TextSpan span =
(textWidget.text as TextSpan).children![0] as TextSpan;
(textWidget.textSpan! as TextSpan).children![0] as TextSpan;
final WidgetSpan widgetSpan = span.children![0] as WidgetSpan;
expect(widgetSpan.child, isInstanceOf<Container>());
},
Expand All @@ -100,8 +100,8 @@ void defineTests() {
),
);

final RichText textWidget = tester.widget(find.byType(RichText));
final TextSpan rootSpan = textWidget.text as TextSpan;
final Text textWidget = tester.widget(find.byType(Text));
final TextSpan rootSpan = textWidget.textSpan! as TextSpan;
final TextSpan firstSpan = rootSpan.children![0] as TextSpan;
final TextSpan secondSpan = rootSpan.children![1] as TextSpan;
final TextSpan thirdSpan = rootSpan.children![2] as TextSpan;
Expand All @@ -114,7 +114,7 @@ void defineTests() {
});

testWidgets(
'TextSpan and WidgetSpan as children in RichText are handled correctly',
'TextSpan and WidgetSpan as children in Text.rich are handled correctly',
(WidgetTester tester) async {
await tester.pumpWidget(
boilerplate(
Expand All @@ -129,8 +129,8 @@ void defineTests() {
),
);

final RichText textWidget = tester.widget(find.byType(RichText));
final TextSpan textSpan = textWidget.text as TextSpan;
final Text textWidget = tester.widget(find.byType(Text));
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;
Expand Down Expand Up @@ -201,7 +201,7 @@ class SubscriptBuilder extends MarkdownElementBuilder {
for (int i = 0; i < textContent.length; i++) {
text += _subscripts[int.parse(textContent[i])];
}
return RichText(text: TextSpan(text: text));
return Text.rich(TextSpan(text: text));
}
}

Expand All @@ -225,11 +225,9 @@ class WikilinkSyntax extends md.InlineSyntax {
class WikilinkBuilder extends MarkdownElementBuilder {
@override
Widget visitElementAfter(md.Element element, _) {
return RichText(
text: TextSpan(
text: element.textContent,
recognizer: TapGestureRecognizer()..onTap = () {}),
);
return Text.rich(TextSpan(
text: element.textContent,
recognizer: TapGestureRecognizer()..onTap = () {}));
}
}

Expand All @@ -250,8 +248,8 @@ class ContainerSyntax extends md.InlineSyntax {
class ContainerBuilder extends MarkdownElementBuilder {
@override
Widget? visitElementAfter(md.Element element, _) {
return RichText(
text: TextSpan(
return Text.rich(
TextSpan(
children: <InlineSpan>[
WidgetSpan(
child: Container(),
Expand All @@ -265,8 +263,8 @@ class ContainerBuilder extends MarkdownElementBuilder {
class ContainerBuilder2 extends MarkdownElementBuilder {
@override
Widget? visitElementAfter(md.Element element, _) {
return RichText(
text: TextSpan(
return Text.rich(
TextSpan(
children: <InlineSpan>[
const TextSpan(text: 'foo'),
WidgetSpan(
Expand Down Expand Up @@ -320,8 +318,8 @@ class InlineTextColorElementBuilder extends MarkdownElementBuilder {
};
final Color? contentColor = contentColors[color];

return RichText(
text: TextSpan(
return Text.rich(
TextSpan(
text: innerText,
style: parentStyle?.copyWith(color: contentColor),
),
Expand Down
Loading