From 93807900409b23b0dd3e1e474b5a9cb5dfc20737 Mon Sep 17 00:00:00 2001 From: Joel Brostrom Date: Tue, 21 Apr 2026 16:44:50 +0200 Subject: [PATCH] Fix hr tag ignoring custom builders and paddingBuilders The hr branch in visitElementAfter unconditionally overwrote the child widget with a default Container, discarding any widget returned by a custom builder registered for 'hr'. Additionally, paddingBuilders entries for 'hr' were never applied because hr is a self-closing block element that never reaches the paddingBuilders check inside _addAnonymousBlockIfNeeded. --- lib/src/builder.dart | 11 +++++- test/horizontal_rule_test.dart | 66 +++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/lib/src/builder.dart b/lib/src/builder.dart index 31a7c95..86f6a89 100644 --- a/lib/src/builder.dart +++ b/lib/src/builder.dart @@ -474,7 +474,16 @@ class MarkdownBuilder implements md.NodeVisitor { child: child, ); } else if (tag == 'hr') { - child = Container(decoration: styleSheet.horizontalRuleDecoration); + if (!builders.containsKey(tag)) { + child = Container(decoration: styleSheet.horizontalRuleDecoration); + } + } + + if (tag == 'hr' && paddingBuilders.containsKey(tag)) { + child = Padding( + padding: paddingBuilders[tag]!.getPadding(), + child: child, + ); } _addBlockChild(child); diff --git a/test/horizontal_rule_test.dart b/test/horizontal_rule_test.dart index 03a465a..79c4311 100644 --- a/test/horizontal_rule_test.dart +++ b/test/horizontal_rule_test.dart @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_markdown_plus/flutter_markdown_plus.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:markdown/markdown.dart' as md; import 'utils.dart'; void main() => defineTests(); @@ -84,5 +85,68 @@ void defineTests() { ]); }, ); + testWidgets( + 'custom builder for hr is respected', + (WidgetTester tester) async { + const String data = '---'; + await tester.pumpWidget( + boilerplate( + MarkdownBody( + data: data, + builders: { + 'hr': _CustomHrBuilder(), + }, + ), + ), + ); + + expect(find.byType(ColoredBox), findsOneWidget); + final ColoredBox box = tester.widget(find.byType(ColoredBox)); + expect(box.color, Colors.red); + }, + ); + + testWidgets( + 'paddingBuilders for hr is applied', + (WidgetTester tester) async { + const String data = '---'; + const double paddingX = 16.0; + await tester.pumpWidget( + boilerplate( + MarkdownBody( + data: data, + paddingBuilders: { + 'hr': _CustomPaddingBuilder(paddingX), + }, + ), + ), + ); + + final Finder paddingFinder = find.byType(Padding); + final List paddings = tester.widgetList(paddingFinder).toList(); + final bool hasHrPadding = paddings.any( + (Padding p) => p.padding.along(Axis.horizontal) == paddingX * 2, + ); + expect(hasHrPadding, isTrue); + }, + ); }); } + +class _CustomHrBuilder extends MarkdownElementBuilder { + @override + Widget visitElementAfter(md.Element element, TextStyle? preferredStyle) { + return const ColoredBox(color: Colors.red, child: SizedBox(height: 2, width: double.infinity)); + } +} + +class _CustomPaddingBuilder extends MarkdownPaddingBuilder { + _CustomPaddingBuilder(this.paddingX); + + final double paddingX; + + @override + EdgeInsets getPadding() { + return EdgeInsets.symmetric(horizontal: paddingX); + } +}