diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md index 3ce5c52c1a7..f3ed8654f36 100644 --- a/packages/flutter_markdown/CHANGELOG.md +++ b/packages/flutter_markdown/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.6.22 + +* Introduces a new `MarkdownElementBuilder.isBlockElement()` method to specify if custom element + is a block. + ## 0.6.21+1 * Adds `onSelectionChanged` to the constructors of `Markdown` and `MarkdownBody`. diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 8ecdd7b9b3f..739520588db 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -10,7 +10,7 @@ import '_functions_io.dart' if (dart.library.js_interop) '_functions_web.dart'; import 'style_sheet.dart'; import 'widget.dart'; -const List _kBlockTags = [ +final List _kBlockTags = [ 'p', 'h1', 'h2', @@ -190,6 +190,12 @@ class MarkdownBuilder implements md.NodeVisitor { _linkHandlers.clear(); _isInBlockquote = false; + builders.forEach((String key, MarkdownElementBuilder value) { + if (value.isBlockElement()) { + _kBlockTags.add(key); + } + }); + _blocks.add(_BlockElement(null)); for (final md.Node node in nodes) { diff --git a/packages/flutter_markdown/lib/src/widget.dart b/packages/flutter_markdown/lib/src/widget.dart index 8edc949242e..38ffbdcc32e 100644 --- a/packages/flutter_markdown/lib/src/widget.dart +++ b/packages/flutter_markdown/lib/src/widget.dart @@ -70,6 +70,11 @@ abstract class SyntaxHighlighter { /// An interface for an element builder. abstract class MarkdownElementBuilder { + /// For block syntax has to return true. + /// + /// By default returns false. + bool isBlockElement() => false; + /// Called when an Element has been reached, before its children have been /// visited. void visitElementBefore(md.Element element) {} diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml index 20639b245fa..c077bb64f93 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.21+1 +version: 0.6.22 environment: sdk: ^3.3.0 diff --git a/packages/flutter_markdown/test/custom_syntax_test.dart b/packages/flutter_markdown/test/custom_syntax_test.dart index 2bee4ebbf0b..28d55cdfd0b 100644 --- a/packages/flutter_markdown/test/custom_syntax_test.dart +++ b/packages/flutter_markdown/test/custom_syntax_test.dart @@ -35,6 +35,30 @@ void defineTests() { }, ); + testWidgets( + 'Custom block element', + (WidgetTester tester) async { + const String blockContent = 'note block'; + await tester.pumpWidget( + boilerplate( + Markdown( + data: '[!NOTE] $blockContent', + extensionSet: md.ExtensionSet.none, + blockSyntaxes: [NoteSyntax()], + builders: { + 'note': NoteBuilder(), + }, + ), + ), + ); + final ColoredBox container = + tester.widgetList(find.byType(ColoredBox)).first as ColoredBox; + expect(container.color, Colors.red); + expect(container.child, isInstanceOf()); + expect((container.child! as Text).data, blockContent); + }, + ); + testWidgets( 'link for wikistyle', (WidgetTester tester) async { @@ -331,3 +355,28 @@ class ImgBuilder extends MarkdownElementBuilder { return Text('foo', style: preferredStyle); } } + +class NoteBuilder extends MarkdownElementBuilder { + @override + Widget? visitText(md.Text text, TextStyle? preferredStyle) { + return ColoredBox( + color: Colors.red, child: Text(text.text, style: preferredStyle)); + } + + @override + bool isBlockElement() { + return true; + } +} + +class NoteSyntax extends md.BlockSyntax { + @override + md.Node? parse(md.BlockParser parser) { + final md.Line line = parser.current; + parser.advance(); + return md.Element('note', [md.Text(line.content.substring(8))]); + } + + @override + RegExp get pattern => RegExp(r'^\[!NOTE] '); +}