Skip to content

Writing an inline syntax/builder for underlined text #20

@code-enthusiast707

Description

@code-enthusiast707

Requirement:
I'm looking to implement underlined text with flutter_markdown_plus using a custom syntax and builder.
While the implementation below does successfully make the text underline, I lose inner-text styling.

I am using 'ins' tag to represent underline text in markdown.

It works well when I don't have any markdown nesting inside of 'ins' tag.

Example input: ~~Strike ***StrikeBoldItalic <ins>StrikeBoldItalicUnderline</ins>***~~
Result: The result is as expected Strike StrikeBoldItalic StrikeBoldItalicUnderline

But in case when I have markdown nesting inside of 'ins' tag then I lose the inner styling.

Example input: <ins>~~***StrikeBoldItalicUnderline***~~</ins>
Expected result: StrikeBoldItalicUnderline
Actual result: StrikeBoldItalicUnderline

Is there a way to achieve the expected result?

Code
import 'package:flutter/material.dart';
import 'package:flutter_markdown_plus/flutter_markdown_plus.dart';
import 'package:markdown/markdown.dart' as md;

void main() {
  runApp(const MarkdownApp());
}

class MarkdownApp extends StatelessWidget {
  const MarkdownApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Markdown App',
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Markdown Render'),
      ),
      body: Markdown(
        data: '1. ~~***<ins>BoldItalicUnderlineStrike</ins>***~~\n2. <ins>~~***BoldItalicUnderlineStrike***~~</ins>',
        inlineSyntaxes: [
          CustomSyntax('ins'),
        ],
        builders: {
          'ins': CustomInlineBuilder(),
        },
      ),
    );
  }
}

class CustomSyntax extends md.InlineSyntax {
  final String tagName;

  CustomSyntax(this.tagName):super('<$tagName>(.*?)</$tagName>');

  @override
  bool onMatch(md.InlineParser parser, Match match) {
    final tagContent = match.group(1)!;
    final subParser = md.InlineParser(tagContent, parser.document);
    final children = subParser.parse();

    final element = md.Element(tagName, []);

    element.children!.addAll(children);
    parser.addNode(element);
    return true;
  }
}

class CustomInlineBuilder extends MarkdownElementBuilder {
  @override
  Widget? visitElementAfterWithContext(BuildContext context, md.Element element, TextStyle? preferredStyle, TextStyle? parentStyle) {
    TextDecoration result = TextDecoration.none;
    if(parentStyle!.decoration != null) {
      result = TextDecoration.combine([parentStyle.decoration!, TextDecoration.underline]);
    } else {
      result = TextDecoration.underline;
    }

    final mergedStyle = parentStyle!.copyWith(decoration: result);
    return Text(element.textContent, style: mergedStyle);
  }
}
Dependencies
dependencies:
  markdown: ^7.3.0
  flutter_markdown_plus: ^1.0.2
Flutter doctor output
[✓] Flutter (Channel stable, 3.29.3, on Ubuntu 24.04.1 LTS 6.8.0-51-generic, locale en_IN) [252ms]
    • Flutter version 3.29.3 on channel stable at /home/user/snap/flutter/common/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision ea121f8859 (6 weeks ago), 2025-04-11 19:10:07 +0000
    • Engine revision cf56914b32
    • Dart version 3.7.2
    • DevTools version 2.42.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions