LaTeX Support

GptMarkdown renders standard LaTeX with its included default math renderer. latexBuilder is an optional replacement hook when your app needs different math UI or error handling.

How it works

The default renderer uses a safe fallback for invalid TeX. Add a latexBuilder only when you need a custom renderer or custom fallback:

latex_builder.dart
1// gpt_markdown includes a default Math.tex renderer with an error fallback. 2// No configuration is needed for standard inline or block math: 3GptMarkdown(r'The formula is \( E = mc^2 \).') 4 5// latexBuilder is an override hook. Add flutter_math_fork (or another renderer) 6// to your app only when you want to replace the default: 7import 'package:flutter_math_fork/flutter_math.dart'; 8 9GptMarkdown( 10 reply, 11 latexBuilder: (context, tex, textStyle, inline) => Math.tex( 12 tex, 13 textStyle: textStyle, 14 onErrorFallback: (err) => Text(tex, style: textStyle), 15 ), 16)

Delimiters

Two delimiter pairs are always active — no configuration needed:

ModeDefault syntaxDollar-sign syntax (opt-in)
Inline\( … \)$…$
Block\[ … \]$$…$$
delimiters.dart
1// \( … \) — always on, no configuration needed. 2GptMarkdown(r'The formula is \( E = mc^2 \) — inline.') 3 4// \[ … \] — block, always on. 5GptMarkdown(r''' 6The quadratic formula: 7\[ x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} \] 8''')

Dollar-sign syntax risk

Warning: Enable useDollarSignsForLatex only when your content will not contain prices or currency symbols. $5 and $10 would be parsed as a math expression.
dollar_signs.dart
1// Dollar-sign syntax is OFF by default. 2// Enable only when your content will not contain prices or currency. 3 4GptMarkdown( 5 r''' 6 Inline: $E = mc^2$ 7 8 Block: 9 $$ 10 \int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2} 11 $$ 12 ''', 13 useDollarSignsForLatex: true, 14) 15 16// WARNING: leave this off if the text may contain prices. 17// "$5 and $10" would be interpreted as math, not currency.

Inline vs block in the builder

The inline parameter tells you which context you are in so you can choose display-mode sizing and centering for block equations:

inline_vs_block.dart
1latexBuilder: (context, tex, textStyle, inline) { 2 // inline = true → rendered inside a paragraph (\( … \) or $…$) 3 // inline = false → rendered as a display-mode block (\[ … \] or $$…$$) 4 if (inline) { 5 return Math.tex(tex, textStyle: textStyle, 6 onErrorFallback: (e) => Text(tex, style: textStyle)); 7 } 8 return Center( 9 child: Math.tex( 10 tex, 11 textStyle: textStyle?.copyWith(fontSize: 20), 12 onErrorFallback: (e) => Text(tex, style: textStyle), 13 ), 14 ); 15}

Fallback on parse error

LLM-produced TeX is not always valid. The built-in renderer already falls back to the raw string. If you replace it with latexBuilder, provide an equivalent onErrorFallback:

fallback.dart
1// Always provide onErrorFallback — the TeX from an LLM is not always valid. 2latexBuilder: (context, tex, textStyle, inline) { 3 return Math.tex( 4 tex, 5 textStyle: textStyle, 6 onErrorFallback: (err) { 7 // Render the raw string so the user still sees something. 8 return Text(tex, style: textStyle); 9 }, 10 ); 11}

Horizontal scroll for wide equations

Rendered math cannot wrap. A wide block equation overflows on a phone screen. Either wrap it yourself in latexBuilder, or set LatexStyle(scrollBlockHorizontally: true):

horizontal_scroll.dart
1// Wide block equations overflow on phones. 2// Option 1: handle it yourself in latexBuilder. 3latexBuilder: (context, tex, textStyle, inline) { 4 final math = Math.tex( 5 tex, 6 textStyle: textStyle, 7 onErrorFallback: (err) => Text(tex, style: textStyle), 8 ); 9 if (inline) return math; 10 return SingleChildScrollView( 11 scrollDirection: Axis.horizontal, 12 child: math, 13 ); 14} 15 16// Option 2: let the package scroll its built-in renderer. 17GptMarkdown( 18 reply, 19 styleSheet: const GptMarkdownStyleSheet( 20 latex: LatexStyle(scrollBlockHorizontally: true), 21 ), 22)

Normalising AI output

Some models double-escape backslashes (\\( instead of \(). Use latexWorkaround to normalise the string before the delimiters are parsed:

workaround.dart
1// Some models (e.g. GPT-4) double-escape backslashes: \\( instead of \(. 2// Normalise with latexWorkaround before the delimiters are parsed. 3GptMarkdown( 4 aiResponse, 5 latexWorkaround: (tex) => tex.replaceAll('\\\\', '\\'), 6)