diff --git a/packages/flutter_markdown/.github/workflows/flutter_ci.yml b/packages/flutter_markdown/.github/workflows/flutter_ci.yml index a848685af0..12e1facb5c 100644 --- a/packages/flutter_markdown/.github/workflows/flutter_ci.yml +++ b/packages/flutter_markdown/.github/workflows/flutter_ci.yml @@ -31,4 +31,5 @@ jobs: with: channel: ${{ matrix.flutter_version }} - run: flutter analyze + - run: flutter format --dry-run --set-exit-if-changed . - run: flutter test diff --git a/packages/flutter_markdown/lib/src/_functions_io.dart b/packages/flutter_markdown/lib/src/_functions_io.dart index 4ece8c1147..047988b00f 100644 --- a/packages/flutter_markdown/lib/src/_functions_io.dart +++ b/packages/flutter_markdown/lib/src/_functions_io.dart @@ -64,7 +64,8 @@ final MarkdownStyleSheet Function(BuildContext, MarkdownStyleSheetBaseTheme?) ); }; -Widget _handleDataSchemeUri(Uri uri, final double? width, final double? height) { +Widget _handleDataSchemeUri( + Uri uri, final double? width, final double? height) { final String mimeType = uri.data!.mimeType; if (mimeType.startsWith('image/')) { return Image.memory( diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart index 9083ec2565..2e6f8300e1 100644 --- a/packages/flutter_markdown/lib/src/builder.dart +++ b/packages/flutter_markdown/lib/src/builder.dart @@ -10,7 +10,25 @@ import '_functions_io.dart' if (dart.library.html) '_functions_web.dart'; import 'style_sheet.dart'; import 'widget.dart'; -const List _kBlockTags = const ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'blockquote', 'pre', 'ol', 'ul', 'hr', 'table', 'thead', 'tbody', 'tr']; +const List _kBlockTags = const [ + 'p', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'li', + 'blockquote', + 'pre', + 'ol', + 'ul', + 'hr', + 'table', + 'thead', + 'tbody', + 'tr' +]; const List _kListTags = const ['ul', 'ol']; @@ -174,14 +192,16 @@ class MarkdownBuilder implements md.NodeVisitor { _addAnonymousBlockIfNeeded(); if (_isListTag(tag)) { _listIndents.add(tag); - if (element.attributes["start"] != null) start = int.parse(element.attributes["start"]!) - 1; + if (element.attributes["start"] != null) + start = int.parse(element.attributes["start"]!) - 1; } else if (tag == 'blockquote') { _isInBlockquote = true; } else if (tag == 'table') { _tables.add(_TableElement()); } else if (tag == 'tr') { final length = _tables.single.rows.length; - BoxDecoration? decoration = styleSheet.tableCellsDecoration as BoxDecoration?; + BoxDecoration? decoration = + styleSheet.tableCellsDecoration as BoxDecoration?; if (length == 0 || length % 2 == 1) decoration = null; _tables.single.rows.add(TableRow( decoration: decoration, @@ -211,7 +231,9 @@ class MarkdownBuilder implements md.NodeVisitor { // The Markdown parser passes empty table data tags for blank // table cells. Insert a text node with an empty string in this // case for the table cell to get properly created. - if (element.tag == 'td' && element.children != null && element.children!.isEmpty) { + if (element.tag == 'td' && + element.children != null && + element.children!.isEmpty) { element.children!.add(md.Text('')); } @@ -226,7 +248,13 @@ class MarkdownBuilder implements md.NodeVisitor { } String? extractTextFromElement(element) { - return element is md.Element && (element.children?.isNotEmpty ?? false) ? element.children!.map((e) => e is md.Text ? e.text : extractTextFromElement(e)).join("") : ((element.attributes?.isNotEmpty ?? false) ? element.attributes["alt"] : ""); + return element is md.Element && (element.children?.isNotEmpty ?? false) + ? element.children! + .map((e) => e is md.Text ? e.text : extractTextFromElement(e)) + .join("") + : ((element.attributes?.isNotEmpty ?? false) + ? element.attributes["alt"] + : ""); } @override @@ -262,7 +290,8 @@ class MarkdownBuilder implements md.NodeVisitor { Widget? child; if (_blocks.isNotEmpty && builders.containsKey(_blocks.last.tag)) { - child = builders[_blocks.last.tag!]!.visitText(text, styleSheet.styles[_blocks.last.tag!]); + child = builders[_blocks.last.tag!]! + .visitText(text, styleSheet.styles[_blocks.last.tag!]); } else if (_blocks.last.tag == 'pre') { child = Scrollbar( child: SingleChildScrollView( @@ -274,7 +303,9 @@ class MarkdownBuilder implements md.NodeVisitor { } else { child = _buildRichText( TextSpan( - style: _isInBlockquote ? styleSheet.blockquote!.merge(_inlines.last.style) : _inlines.last.style, + style: _isInBlockquote + ? styleSheet.blockquote!.merge(_inlines.last.style) + : _inlines.last.style, text: _isInBlockquote ? text.text : trimText(text.text), recognizer: _linkHandlers.isNotEmpty ? _linkHandlers.last : null, ), @@ -298,7 +329,9 @@ class MarkdownBuilder implements md.NodeVisitor { if (current.children.isNotEmpty) { child = Column( - crossAxisAlignment: fitContent ? CrossAxisAlignment.start : CrossAxisAlignment.stretch, + crossAxisAlignment: fitContent + ? CrossAxisAlignment.start + : CrossAxisAlignment.stretch, children: current.children, ); } else { @@ -322,11 +355,19 @@ class MarkdownBuilder implements md.NodeVisitor { bullet = _buildBullet(_listIndents.last); } child = Row( - textBaseline: listItemCrossAxisAlignment == MarkdownListItemCrossAxisAlignment.start ? null : TextBaseline.alphabetic, - crossAxisAlignment: listItemCrossAxisAlignment == MarkdownListItemCrossAxisAlignment.start ? CrossAxisAlignment.start : CrossAxisAlignment.baseline, + textBaseline: listItemCrossAxisAlignment == + MarkdownListItemCrossAxisAlignment.start + ? null + : TextBaseline.alphabetic, + crossAxisAlignment: listItemCrossAxisAlignment == + MarkdownListItemCrossAxisAlignment.start + ? CrossAxisAlignment.start + : CrossAxisAlignment.baseline, children: [ SizedBox( - width: styleSheet.listIndent! + styleSheet.listBulletPadding!.left + styleSheet.listBulletPadding!.right, + width: styleSheet.listIndent! + + styleSheet.listBulletPadding!.left + + styleSheet.listBulletPadding!.right, child: bullet, ), Expanded(child: child) @@ -364,7 +405,8 @@ class MarkdownBuilder implements md.NodeVisitor { final _InlineElement parent = _inlines.last; if (builders.containsKey(tag)) { - final Widget? child = builders[tag]!.visitElementAfter(element, styleSheet.styles[tag]); + final Widget? child = + builders[tag]!.visitElementAfter(element, styleSheet.styles[tag]); if (child != null) current.children[0] = child; } else if (tag == 'img') { // create an image widget for this image @@ -436,7 +478,8 @@ class MarkdownBuilder implements md.NodeVisitor { } if (_linkHandlers.isNotEmpty) { - TapGestureRecognizer recognizer = _linkHandlers.last as TapGestureRecognizer; + TapGestureRecognizer recognizer = + _linkHandlers.last as TapGestureRecognizer; return GestureDetector(child: child, onTap: recognizer.onTap); } else { return child; @@ -464,7 +507,8 @@ class MarkdownBuilder implements md.NodeVisitor { if (bulletBuilder != null) { return Padding( padding: styleSheet.listBulletPadding!, - child: bulletBuilder!(index, isUnordered ? BulletStyle.unorderedList : BulletStyle.orderedList), + child: bulletBuilder!(index, + isUnordered ? BulletStyle.unorderedList : BulletStyle.orderedList), ); } @@ -553,20 +597,28 @@ class MarkdownBuilder implements md.NodeVisitor { ) { List mergedTexts = []; for (Widget child in children) { - if (mergedTexts.isNotEmpty && mergedTexts.last is RichText && child is RichText) { + if (mergedTexts.isNotEmpty && + mergedTexts.last is RichText && + child is RichText) { RichText previous = mergedTexts.removeLast() as RichText; TextSpan previousTextSpan = previous.text as TextSpan; - List children = previousTextSpan.children != null ? List.from(previousTextSpan.children!) : [previousTextSpan]; + List children = previousTextSpan.children != null + ? List.from(previousTextSpan.children!) + : [previousTextSpan]; children.add(child.text as TextSpan); TextSpan? mergedSpan = _mergeSimilarTextSpans(children); mergedTexts.add(_buildRichText( mergedSpan, textAlign: textAlign, )); - } else if (mergedTexts.isNotEmpty && mergedTexts.last is SelectableText && child is SelectableText) { + } else if (mergedTexts.isNotEmpty && + mergedTexts.last is SelectableText && + child is SelectableText) { SelectableText previous = mergedTexts.removeLast() as SelectableText; TextSpan previousTextSpan = previous.textSpan!; - List children = previousTextSpan.children != null ? List.from(previousTextSpan.children!) : [previousTextSpan]; + List children = previousTextSpan.children != null + ? List.from(previousTextSpan.children!) + : [previousTextSpan]; if (child.textSpan != null) { children.add(child.textSpan!); } @@ -629,7 +681,10 @@ class MarkdownBuilder implements md.NodeVisitor { for (int index = 1; index < textSpans.length; index++) { TextSpan? nextChild = textSpans[index]; - if (nextChild is TextSpan && nextChild.recognizer == mergedSpans.last.recognizer && nextChild.semanticsLabel == mergedSpans.last.semanticsLabel && nextChild.style == mergedSpans.last.style) { + if (nextChild is TextSpan && + nextChild.recognizer == mergedSpans.last.recognizer && + nextChild.semanticsLabel == mergedSpans.last.semanticsLabel && + nextChild.style == mergedSpans.last.style) { TextSpan previous = mergedSpans.removeLast(); mergedSpans.add(TextSpan( text: previous.toPlainText() + nextChild.toPlainText(), @@ -644,7 +699,9 @@ class MarkdownBuilder implements md.NodeVisitor { // 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.length == 1 + ? mergedSpans.first + : TextSpan(children: mergedSpans); } Widget _buildRichText(TextSpan? text, {TextAlign? textAlign}) { diff --git a/packages/flutter_markdown/lib/src/widget.dart b/packages/flutter_markdown/lib/src/widget.dart index 060861dae1..5595774eb5 100644 --- a/packages/flutter_markdown/lib/src/widget.dart +++ b/packages/flutter_markdown/lib/src/widget.dart @@ -74,7 +74,8 @@ abstract class MarkdownElementBuilder { /// to [preferredStyle]. /// /// If you needn't build a widget, return null. - Widget? visitElementAfter(md.Element element, TextStyle? preferredStyle) => null; + Widget? visitElementAfter(md.Element element, TextStyle? preferredStyle) => + null; } /// Enum to specify which theme being used when creating [MarkdownStyleSheet] @@ -151,7 +152,8 @@ abstract class MarkdownWidget extends StatefulWidget { this.bulletBuilder, this.builders = const {}, this.fitContent = false, - this.listItemCrossAxisAlignment = MarkdownListItemCrossAxisAlignment.baseline, + this.listItemCrossAxisAlignment = + MarkdownListItemCrossAxisAlignment.baseline, }) : super(key: key); /// The Markdown to display. @@ -238,7 +240,8 @@ abstract class MarkdownWidget extends StatefulWidget { _MarkdownWidgetState createState() => _MarkdownWidgetState(); } -class _MarkdownWidgetState extends State implements MarkdownBuilderDelegate { +class _MarkdownWidgetState extends State + implements MarkdownBuilderDelegate { List? _children; final List _recognizers = []; @@ -251,7 +254,8 @@ class _MarkdownWidgetState extends State implements MarkdownBuil @override void didUpdateWidget(MarkdownWidget oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.data != oldWidget.data || widget.styleSheet != oldWidget.styleSheet) { + if (widget.data != oldWidget.data || + widget.styleSheet != oldWidget.styleSheet) { _parseMarkdown(); } } @@ -263,8 +267,10 @@ class _MarkdownWidgetState extends State implements MarkdownBuil } void _parseMarkdown() { - final MarkdownStyleSheet fallbackStyleSheet = kFallbackStyle(context, widget.styleSheetTheme); - final MarkdownStyleSheet styleSheet = fallbackStyleSheet.merge(widget.styleSheet); + final MarkdownStyleSheet fallbackStyleSheet = + kFallbackStyle(context, widget.styleSheetTheme); + final MarkdownStyleSheet styleSheet = + fallbackStyleSheet.merge(widget.styleSheet); _disposeRecognizers(); @@ -300,7 +306,8 @@ class _MarkdownWidgetState extends State implements MarkdownBuil void _disposeRecognizers() { if (_recognizers.isEmpty) return; - final List localRecognizers = List.from(_recognizers); + final List localRecognizers = + List.from(_recognizers); _recognizers.clear(); for (GestureRecognizer recognizer in localRecognizers) recognizer.dispose(); } @@ -358,7 +365,8 @@ class MarkdownBody extends MarkdownWidget { MarkdownCheckboxBuilder? checkboxBuilder, MarkdownBulletBuilder? bulletBuilder, Map builders = const {}, - MarkdownListItemCrossAxisAlignment listItemCrossAxisAlignment = MarkdownListItemCrossAxisAlignment.baseline, + MarkdownListItemCrossAxisAlignment listItemCrossAxisAlignment = + MarkdownListItemCrossAxisAlignment.baseline, this.shrinkWrap = true, this.fitContent = true, }) : super( @@ -392,7 +400,8 @@ class MarkdownBody extends MarkdownWidget { if (children!.length == 1) return children.single; return Column( mainAxisSize: shrinkWrap ? MainAxisSize.min : MainAxisSize.max, - crossAxisAlignment: fitContent ? CrossAxisAlignment.start : CrossAxisAlignment.stretch, + crossAxisAlignment: + fitContent ? CrossAxisAlignment.start : CrossAxisAlignment.stretch, children: children, ); } @@ -426,7 +435,8 @@ class Markdown extends MarkdownWidget { MarkdownCheckboxBuilder? checkboxBuilder, MarkdownBulletBuilder? bulletBuilder, Map builders = const {}, - MarkdownListItemCrossAxisAlignment listItemCrossAxisAlignment = MarkdownListItemCrossAxisAlignment.baseline, + MarkdownListItemCrossAxisAlignment listItemCrossAxisAlignment = + MarkdownListItemCrossAxisAlignment.baseline, this.padding = const EdgeInsets.all(16.0), this.controller, this.physics, diff --git a/packages/flutter_markdown/test/custom_syntax_test.dart b/packages/flutter_markdown/test/custom_syntax_test.dart index 81c4ed681e..7ff548fb59 100644 --- a/packages/flutter_markdown/test/custom_syntax_test.dart +++ b/packages/flutter_markdown/test/custom_syntax_test.dart @@ -51,7 +51,8 @@ void defineTests() { ); final RichText textWidget = tester.widget(find.byType(RichText)); - final TextSpan span = (textWidget.text as TextSpan).children![1] as TextSpan; + final TextSpan span = + (textWidget.text as TextSpan).children![1] as TextSpan; expect(span.children, null); expect(span.recognizer.runtimeType, equals(TapGestureRecognizer)); diff --git a/packages/flutter_markdown/test/list_test.dart b/packages/flutter_markdown/test/list_test.dart index f64bad9763..b16c111481 100644 --- a/packages/flutter_markdown/test/list_test.dart +++ b/packages/flutter_markdown/test/list_test.dart @@ -69,7 +69,18 @@ void defineTests() { ); final Iterable widgets = tester.allWidgets; - expectTextStrings(widgets, ['1.', 'Item 1', '2.', 'Item 2', '3.', 'Item 3', '10.', 'Item 10', '11.', 'Item 11']); + expectTextStrings(widgets, [ + '1.', + 'Item 1', + '2.', + 'Item 2', + '3.', + 'Item 3', + '10.', + 'Item 10', + '11.', + 'Item 11' + ]); }, ); }); @@ -98,7 +109,8 @@ void defineTests() { testWidgets('custom bullet builder', (WidgetTester tester) async { final String data = '* Item 1\n* Item 2\n1) Item 3\n2) Item 4'; - final MarkdownBulletBuilder builder = (int index, BulletStyle style) => Text('$index ${style == BulletStyle.orderedList ? 'ordered' : 'unordered'}'); + final MarkdownBulletBuilder builder = (int index, BulletStyle style) => Text( + '$index ${style == BulletStyle.orderedList ? 'ordered' : 'unordered'}'); await tester.pumpWidget( boilerplate( @@ -124,7 +136,8 @@ void defineTests() { 'custom checkbox builder', (WidgetTester tester) async { const String data = '- [x] Item 1\n- [ ] Item 2'; - final MarkdownCheckboxBuilder builder = (bool checked) => Text('$checked'); + final MarkdownCheckboxBuilder builder = + (bool checked) => Text('$checked'); await tester.pumpWidget( boilerplate( diff --git a/packages/flutter_markdown/test/style_sheet_test.dart b/packages/flutter_markdown/test/style_sheet_test.dart index 0a76144779..f1be13466d 100644 --- a/packages/flutter_markdown/test/style_sheet_test.dart +++ b/packages/flutter_markdown/test/style_sheet_test.dart @@ -60,7 +60,8 @@ void defineTests() { // code expect(style.code!.color, cTheme.textTheme.textStyle.color); - expect(style.code!.fontSize, cTheme.textTheme.textStyle.fontSize! * 0.85); + expect( + style.code!.fontSize, cTheme.textTheme.textStyle.fontSize! * 0.85); expect(style.code!.fontFamily, 'monospace'); expect( style.code!.backgroundColor, CupertinoColors.systemGrey6.darkColor); @@ -149,7 +150,8 @@ void defineTests() { // code expect(style.code!.color, theme.textTheme.bodyText2!.color); - expect(style.code!.fontSize, theme.textTheme.bodyText2!.fontSize! * 0.85); + expect( + style.code!.fontSize, theme.textTheme.bodyText2!.fontSize! * 0.85); expect(style.code!.fontFamily, 'monospace'); expect(style.code!.backgroundColor, theme.cardColor); diff --git a/packages/flutter_markdown/test/table_test.dart b/packages/flutter_markdown/test/table_test.dart index 37822905b9..3026461495 100644 --- a/packages/flutter_markdown/test/table_test.dart +++ b/packages/flutter_markdown/test/table_test.dart @@ -74,8 +74,8 @@ void defineTests() { ); final Iterable widgets = tester.allWidgets; - final RichText richText = - widgets.lastWhere((Widget widget) => widget is RichText) as RichText; + final RichText richText = widgets + .lastWhere((Widget widget) => widget is RichText) as RichText; expectTextStrings(widgets, ['Header', 'italic']); expect(richText.text.style!.fontStyle, FontStyle.italic); diff --git a/packages/flutter_markdown/test/text_alignment_test.dart b/packages/flutter_markdown/test/text_alignment_test.dart index 15c64bca15..30369a4b8d 100644 --- a/packages/flutter_markdown/test/text_alignment_test.dart +++ b/packages/flutter_markdown/test/text_alignment_test.dart @@ -74,7 +74,8 @@ void defineTests() { ), ); - final RichText text = tester.widgetList(find.byType(RichText)).single as RichText; + final RichText text = + tester.widgetList(find.byType(RichText)).single as RichText; expect(text.textAlign, TextAlign.justify); }, ); @@ -103,8 +104,9 @@ void defineTests() { ), ); - final SelectableText text = - tester.widgetList(find.byType(SelectableText)).single as SelectableText; + final SelectableText text = tester + .widgetList(find.byType(SelectableText)) + .single as SelectableText; expect(text.textAlign, TextAlign.justify); }, ); diff --git a/packages/flutter_markdown/test/uri_test.dart b/packages/flutter_markdown/test/uri_test.dart index 70c9d8168b..f68279464d 100644 --- a/packages/flutter_markdown/test/uri_test.dart +++ b/packages/flutter_markdown/test/uri_test.dart @@ -88,8 +88,8 @@ void defineTests() { ); final Iterable widgets = tester.allWidgets; - final SizedBox widget = - widgets.firstWhere((Widget widget) => widget is SizedBox) as SizedBox; + final SizedBox widget = widgets + .firstWhere((Widget widget) => widget is SizedBox) as SizedBox; expect(widget.runtimeType, SizedBox); }, );