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,
 | 
						|
    );
 | 
						|
  }
 | 
						|
}
 |