[flutter_markdown] Padding builder (#486)
diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md
index 6a6a568..175d7fa 100644
--- a/packages/flutter_markdown/CHANGELOG.md
+++ b/packages/flutter_markdown/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.6.8
+
+ * Added option paddingBuilders
+
## 0.6.7
* Fix `unnecessary_import` lint errors.
diff --git a/packages/flutter_markdown/example/lib/demos/wrap_alignment_demo.dart b/packages/flutter_markdown/example/lib/demos/wrap_alignment_demo.dart
index 74faa29..c3ca366 100644
--- a/packages/flutter_markdown/example/lib/demos/wrap_alignment_demo.dart
+++ b/packages/flutter_markdown/example/lib/demos/wrap_alignment_demo.dart
@@ -5,6 +5,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
+import 'package:markdown/markdown.dart' as md;
import '../shared/dropdown_menu.dart';
import '../shared/markdown_demo_widget.dart';
import '../shared/markdown_extensions.dart';
@@ -127,6 +128,9 @@
blockquoteAlign: _wrapAlignment,
codeblockAlign: _wrapAlignment,
),
+ paddingBuilders: <String, MarkdownPaddingBuilder>{
+ 'p': CustomPaddingBuilder()
+ },
),
),
],
@@ -138,3 +142,28 @@
);
}
}
+
+class CustomPaddingBuilder extends MarkdownPaddingBuilder {
+ final EdgeInsets _padding = const EdgeInsets.only(left: 10.0);
+ bool paddingUse = true;
+
+ @override
+ void visitElementBefore(md.Element element) {
+ if (element.children!.length == 1 && element.children![0] is md.Element) {
+ final md.Element child = element.children![0] as md.Element;
+
+ paddingUse = child.tag != 'img';
+ } else {
+ paddingUse = true;
+ }
+ }
+
+ @override
+ EdgeInsets getPadding() {
+ if (paddingUse) {
+ return _padding;
+ } else {
+ return EdgeInsets.zero;
+ }
+ }
+}
diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart
index a9be31f..c94123f 100644
--- a/packages/flutter_markdown/lib/src/builder.dart
+++ b/packages/flutter_markdown/lib/src/builder.dart
@@ -101,6 +101,7 @@
required this.checkboxBuilder,
required this.bulletBuilder,
required this.builders,
+ required this.paddingBuilders,
required this.listItemCrossAxisAlignment,
this.fitContent = false,
this.onTapText,
@@ -133,6 +134,9 @@
/// Call when build a custom widget.
final Map<String, MarkdownElementBuilder> builders;
+ /// Call when build a padding for widget.
+ final Map<String, MarkdownPaddingBuilder> paddingBuilders;
+
/// Whether to allow the widget to fit the child content.
final bool fitContent;
@@ -195,6 +199,10 @@
builders[tag]!.visitElementBefore(element);
}
+ if (paddingBuilders.containsKey(tag)) {
+ paddingBuilders[tag]!.visitElementBefore(element);
+ }
+
int? start;
if (_isBlockTag(tag)) {
_addAnonymousBlockIfNeeded();
@@ -424,6 +432,11 @@
} else {
final _InlineElement current = _inlines.removeLast();
final _InlineElement parent = _inlines.last;
+ EdgeInsets padding = EdgeInsets.zero;
+
+ if (paddingBuilders.containsKey(tag)) {
+ padding = paddingBuilders[tag]!.getPadding();
+ }
if (builders.containsKey(tag)) {
final Widget? child =
@@ -433,10 +446,13 @@
}
} else if (tag == 'img') {
// create an image widget for this image
- current.children.add(_buildImage(
- element.attributes['src']!,
- element.attributes['title'],
- element.attributes['alt'],
+ current.children.add(_buildPadding(
+ padding,
+ _buildImage(
+ element.attributes['src']!,
+ element.attributes['title'],
+ element.attributes['alt'],
+ ),
));
} else if (tag == 'br') {
current.children.add(_buildRichText(const TextSpan(text: '\n')));
@@ -573,6 +589,14 @@
);
}
+ Widget _buildPadding(EdgeInsets padding, Widget child) {
+ if (padding == EdgeInsets.zero) {
+ return child;
+ }
+
+ return Padding(padding: padding, child: child);
+ }
+
void _addParentInlineIfNeeded(String? tag) {
if (_inlines.isEmpty) {
_inlines.add(_InlineElement(
@@ -603,6 +627,10 @@
blockAlignment = _wrapAlignmentForBlockTag(_currentBlockTag);
textAlign = _textAlignForBlockTag(_currentBlockTag);
textPadding = _textPaddingForBlockTag(_currentBlockTag);
+
+ if (paddingBuilders.containsKey(_currentBlockTag)) {
+ textPadding = paddingBuilders[_currentBlockTag]!.getPadding();
+ }
}
final _InlineElement inline = _inlines.single;
diff --git a/packages/flutter_markdown/lib/src/widget.dart b/packages/flutter_markdown/lib/src/widget.dart
index 447adfd..0e4ca00 100644
--- a/packages/flutter_markdown/lib/src/widget.dart
+++ b/packages/flutter_markdown/lib/src/widget.dart
@@ -163,6 +163,7 @@
this.checkboxBuilder,
this.bulletBuilder,
this.builders = const <String, MarkdownElementBuilder>{},
+ this.paddingBuilders = const <String, MarkdownPaddingBuilder>{},
this.fitContent = false,
this.listItemCrossAxisAlignment =
MarkdownListItemCrossAxisAlignment.baseline,
@@ -234,6 +235,19 @@
/// The `SubscriptBuilder` is a subclass of [MarkdownElementBuilder].
final Map<String, MarkdownElementBuilder> builders;
+ /// Add padding for different tags (use only for block elements and img)
+ ///
+ /// For example, we will add padding for `img` tag:
+ ///
+ /// ```dart
+ /// paddingBuilders: {
+ /// 'img': ImgPaddingBuilder(),
+ /// }
+ /// ```
+ ///
+ /// The `ImgPaddingBuilder` is a subclass of [MarkdownPaddingBuilder].
+ final Map<String, MarkdownPaddingBuilder> paddingBuilders;
+
/// Whether to allow the widget to fit the child content.
final bool fitContent;
@@ -317,6 +331,7 @@
checkboxBuilder: widget.checkboxBuilder,
bulletBuilder: widget.bulletBuilder,
builders: widget.builders,
+ paddingBuilders: widget.paddingBuilders,
fitContent: widget.fitContent,
listItemCrossAxisAlignment: widget.listItemCrossAxisAlignment,
onTapText: widget.onTapText,
@@ -391,6 +406,8 @@
MarkdownBulletBuilder? bulletBuilder,
Map<String, MarkdownElementBuilder> builders =
const <String, MarkdownElementBuilder>{},
+ Map<String, MarkdownPaddingBuilder> paddingBuilders =
+ const <String, MarkdownPaddingBuilder>{},
MarkdownListItemCrossAxisAlignment listItemCrossAxisAlignment =
MarkdownListItemCrossAxisAlignment.baseline,
this.shrinkWrap = true,
@@ -412,6 +429,7 @@
imageBuilder: imageBuilder,
checkboxBuilder: checkboxBuilder,
builders: builders,
+ paddingBuilders: paddingBuilders,
listItemCrossAxisAlignment: listItemCrossAxisAlignment,
bulletBuilder: bulletBuilder,
fitContent: fitContent,
@@ -464,6 +482,8 @@
MarkdownBulletBuilder? bulletBuilder,
Map<String, MarkdownElementBuilder> builders =
const <String, MarkdownElementBuilder>{},
+ Map<String, MarkdownPaddingBuilder> paddingBuilders =
+ const <String, MarkdownPaddingBuilder>{},
MarkdownListItemCrossAxisAlignment listItemCrossAxisAlignment =
MarkdownListItemCrossAxisAlignment.baseline,
this.padding = const EdgeInsets.all(16.0),
@@ -487,6 +507,7 @@
imageBuilder: imageBuilder,
checkboxBuilder: checkboxBuilder,
builders: builders,
+ paddingBuilders: paddingBuilders,
listItemCrossAxisAlignment: listItemCrossAxisAlignment,
bulletBuilder: bulletBuilder,
softLineBreak: softLineBreak,
@@ -541,3 +562,13 @@
return true;
}
}
+
+/// An interface for an padding builder for element.
+abstract class MarkdownPaddingBuilder {
+ /// Called when an Element has been reached, before its children have been
+ /// visited.
+ void visitElementBefore(md.Element element) {}
+
+ /// Called when a widget node has been rendering and need tag padding.
+ EdgeInsets getPadding() => EdgeInsets.zero;
+}
diff --git a/packages/flutter_markdown/pubspec.yaml b/packages/flutter_markdown/pubspec.yaml
index 0ce150c..1dbb23b 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.7
+version: 0.6.8
environment:
sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/flutter_markdown/test/padding_test.dart b/packages/flutter_markdown/test/padding_test.dart
new file mode 100644
index 0000000..6f27ca9
--- /dev/null
+++ b/packages/flutter_markdown/test/padding_test.dart
@@ -0,0 +1,67 @@
+// 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 'utils.dart';
+
+void main() => defineTests();
+
+void defineTests() {
+ group('Padding builders', () {
+ testWidgets(
+ 'use paddingBuilders for p',
+ (WidgetTester tester) async {
+ const double paddingX = 10.0;
+
+ await tester.pumpWidget(
+ boilerplate(
+ Markdown(
+ data: '**line 1**\n\n# H1\n![alt](/assets/images/logo.png)',
+ paddingBuilders: <String, MarkdownPaddingBuilder>{
+ 'p': CustomPaddingBuilder(paddingX * 1),
+ 'strong': CustomPaddingBuilder(paddingX * 2),
+ 'h1': CustomPaddingBuilder(paddingX * 3),
+ 'img': CustomPaddingBuilder(paddingX * 4),
+ }),
+ ),
+ );
+
+ final List<Padding> paddings =
+ tester.widgetList<Padding>(find.byType(Padding)).toList();
+
+ expect(paddings.length, 4);
+ expect(
+ paddings[0].padding.along(Axis.horizontal) == paddingX * 1 * 2,
+ true,
+ );
+ expect(
+ paddings[1].padding.along(Axis.horizontal) == paddingX * 3 * 2,
+ true,
+ );
+ expect(
+ paddings[2].padding.along(Axis.horizontal) == paddingX * 1 * 2,
+ true,
+ );
+ expect(
+ paddings[3].padding.along(Axis.horizontal) == paddingX * 4 * 2,
+ true,
+ );
+ },
+ );
+ });
+}
+
+class CustomPaddingBuilder extends MarkdownPaddingBuilder {
+ CustomPaddingBuilder(this.paddingX);
+
+ double paddingX;
+
+ @override
+ EdgeInsets getPadding() {
+ return EdgeInsets.symmetric(horizontal: paddingX);
+ }
+}