81 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			81 lines
		
	
	
		
			2.6 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import 'package:flutter/material.dart';
 | |
| import 'package:markdown_widget/markdown_widget.dart';
 | |
| import 'package:flutter_math_fork/flutter_math.dart';
 | |
| import 'package:markdown/markdown.dart' as m;
 | |
| 
 | |
| SpanNodeGeneratorWithTag latexGenerator = SpanNodeGeneratorWithTag(
 | |
|   tag: _latexTag,
 | |
|   generator:
 | |
|       (e, config, visitor) => LatexNode(e.attributes, e.textContent, config),
 | |
| );
 | |
| 
 | |
| const _latexTag = 'latex';
 | |
| 
 | |
| class LatexSyntax extends m.InlineSyntax {
 | |
|   final bool isDark;
 | |
|   LatexSyntax(this.isDark) : super(r'(\$\$[\s\S]+\$\$)|(\$.+?\$)');
 | |
| 
 | |
|   @override
 | |
|   bool onMatch(m.InlineParser parser, Match match) {
 | |
|     final input = match.input;
 | |
|     final matchValue = input.substring(match.start, match.end);
 | |
|     String content = '';
 | |
|     bool isInline = true;
 | |
|     const blockSyntax = '\$\$';
 | |
|     const inlineSyntax = '\$';
 | |
|     if (matchValue.startsWith(blockSyntax) &&
 | |
|         matchValue.endsWith(blockSyntax) &&
 | |
|         (matchValue != blockSyntax)) {
 | |
|       content = matchValue.substring(2, matchValue.length - 2);
 | |
|       isInline = false;
 | |
|     } else if (matchValue.startsWith(inlineSyntax) &&
 | |
|         matchValue.endsWith(inlineSyntax) &&
 | |
|         matchValue != inlineSyntax) {
 | |
|       content = matchValue.substring(1, matchValue.length - 1);
 | |
|     }
 | |
|     m.Element el = m.Element.text(_latexTag, matchValue);
 | |
|     el.attributes['content'] = content;
 | |
|     el.attributes['isInline'] = '$isInline';
 | |
|     el.attributes['isDark'] = isDark.toString();
 | |
|     parser.addNode(el);
 | |
|     return true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| class LatexNode extends SpanNode {
 | |
|   final Map<String, String> attributes;
 | |
|   final String textContent;
 | |
|   final MarkdownConfig config;
 | |
| 
 | |
|   LatexNode(this.attributes, this.textContent, this.config);
 | |
| 
 | |
|   @override
 | |
|   InlineSpan build() {
 | |
|     final content = attributes['content'] ?? '';
 | |
|     final isInline = attributes['isInline'] == 'true';
 | |
|     final isDark = attributes['isDark'] == 'true';
 | |
|     final style = parentStyle ?? config.p.textStyle;
 | |
|     if (content.isEmpty) return TextSpan(style: style, text: textContent);
 | |
|     final latex = Math.tex(
 | |
|       content,
 | |
|       mathStyle: MathStyle.text,
 | |
|       textStyle: style.copyWith(color: isDark ? Colors.white : Colors.black),
 | |
|       textScaleFactor: 1,
 | |
|       onErrorFallback: (error) {
 | |
|         return Text(textContent, style: style.copyWith(color: Colors.red));
 | |
|       },
 | |
|     );
 | |
|     return WidgetSpan(
 | |
|       alignment: PlaceholderAlignment.middle,
 | |
|       child:
 | |
|           !isInline
 | |
|               ? Container(
 | |
|                 width: double.infinity,
 | |
|                 margin: EdgeInsets.symmetric(vertical: 16),
 | |
|                 child: Center(child: latex),
 | |
|               )
 | |
|               : latex,
 | |
|     );
 | |
|   }
 | |
| }
 |