flutter_markdownでカスタムのインラインタグを実装する方法
はじめに
flutter_markdownでカスタムのブロックタグを実装する方法
前回はflutter_markdownでカスタムのブロックタグを実装する方法を紹介しました。
今回はインラインタグの実装方法について紹介します。
カスタムのブロックタグを実装した箇所と被っている部分は省略します。
上記のブログをご覧ください。
今回作るもの
テキスト <green_inline content="greenテキスト" /> てきすと
上記のような <green_inline content="..."> があった場合、 content の文字色を緑にするようなものを作っていきます。

実装方法
MarkdownBodyの説明
カスタムのブロックタグの時と同様に MarkdownBody を使って表示していきます。
ブロックタグの際はMarkdownデータをパースする際に使用するカスタムブロックタグの指定は blockSyntaxes で行なっていましたが、インラインブロックの際は inlineSyntaxes に指定します。 Widgetの表示に関しては ブロックタグのサイト同様に builders で指定します。
return fmd.MarkdownBody(
// 表示するMarkdownの文字列
data: content,
inlineSyntaxes: <InlineSyntax>[
// ...
],
builders: <String, MarkdownElementBuilder>{
// ...
},
);
では InlineSyntax を実装していきます。
InlineSyntaxの実装
以下が InlineSyntax の実装例です。
import 'package:markdown/markdown.dart' as md;
class GreenInlineMarkdownSyntax extends md.InlineSyntax {
GreenInlineMarkdownSyntax() : super(r'<green_inline\s+content="([^"]+)".*?\/?>');
@override
bool onMatch(md.InlineParser parser, Match match) {
final content = match.group(1) ?? '';
// カスタムノードを作成
final element = md.Element('green_inline', []);
element.attributes['content'] = content;
parser.addNode(element);
return true;
}
}
1. InlineSyntaxを継承したクラスの作成
カスタムのタグを追加するために、InlineSyntax を継承したクラスを作成します。
2. パターンの定義
コンストラクタの super で <green_inline content="..." /> を識別するための正規表現を定義します。
GreenInlineMarkdownSyntax() : super(r'<green_inline\s+content="([^"]+)".*?\/?>');
正規表現の説明
<green_inline\s+- タグ名が
<green_inlineで始まり、続いて1つ以上の空白文字があることを確認します。
- タグ名が
content="([^"]+)"content属性が存在し、その値をキャプチャします。?= はポジティブ・ルックアヘッドで条件の存在を確認します。
.*?\/?>- 任意の文字列が続き、自己終了タグ(/>)または終了タグ(>)で終わることを確認します。
3. onMatchメソッドの実装
onMatch で一致するタグから値を取得し、カスタムノード(Element)を作成します:
@override
bool onMatch(md.InlineParser parser, Match match) {
final content = match.group(1) ?? '';
// カスタムノードを作成
final element = md.Element('green_inline', []);
element.attributes['content'] = content;
parser.addNode(element);
return true;
}
- 正規表現で一致した値を取得し、
Elementを生成します。 Elementのタグ名はgreen_inlineに設定します(後でMarkdownBodyのbuildersにセットするため)。- 必要な値を
attributesに追加します parser.addNode(element);は、現在のInlineSyntaxが正規表現にマッチした際に、解析結果(ノード)をMarkdownのツリーに追加するために必要です。この操作を行うことで、カスタム構文を適切に処理し、それを後でWidgetとして描画できるようにします。
これで、inlineSyntaxes 側の実装は完了です。
MarkdownElementBuilderの実装
次に、 MarkdownBody の builders に設定する MarkdownElementBuilder の実装について解説します。
class GreenInlineMarkdownBuilder extends fmd.MarkdownElementBuilder {
@override
bool isBlockElement() => false;
@override
Widget? visitElementAfterWithContext(
BuildContext context,
md.Element element,
TextStyle? preferredStyle,
TextStyle? parentStyle,
) {
return Text.rich(
TextSpan(
text: ' ${element.attributes['content']} ',
style: parentStyle?.copyWith(color: Colors.green),
),
);
}
}
1. MarkdownElementBuilderを継承したクラスを作成
カスタム要素をレンダリングするために、 MarkdownElementBuilder を継承したクラスを作成します。
2. isBlockElementのオーバーライド
デフォルトで isBlockElement は false なので、わざわざ指定する必要はありませんが、説明のため追加しています。ブロックタグの場合はここを true にします。
@override
bool isBlockElement() => false;
3. visitElementAfterWithContextの実装
カスタムタグがレンダリングされる際に表示するWidgetを定義します。
テキストは flutter_markdown の中を見てみると TextSpan で表示しているようなので、 Text.rich を使用して、 TextSpan を定義します。
またテキストのスタイルは引数の parentStyle を使って指定しています。これにより親要素のTextStyleを利用して表示することができます。
@override
Widget? visitElementAfterWithContext(
BuildContext context,
md.Element element,
TextStyle? preferredStyle,
TextStyle? parentStyle,
) {
return Text.rich(
TextSpan(
text: '${element.attributes['content']}',
style: parentStyle?.copyWith(color: Colors.green),
),
);
}
MarkdownBodyの実装
これまでに作成した GreenInlineSyntax と GreenInlineBuilder を MarkdownBody にセットします。
return fmd.MarkdownBody(
inlineSyntaxes: const [
GreenInlineSyntax(),
],
builders: {
'green_inline': GreenInlineBuilder(),
},
);
これで実装完了です!!
最後に
カスタムのインラインタグの実装方法について解説しました。カスタムのブロックタグ、インラインタグを定義することで、あらゆる表現ができると思います。
まだまだflutter_markdownの知見があるので、また紹介しようと思います。