You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中渲染含LaTeX公式的Delta格式富文本?

解决Flutter中Quill Delta渲染LaTeX公式的问题

核心思路

要实现Delta中LaTeX公式的渲染,需要两步:识别Delta里的LaTeX内容 + 扩展flutter_quill的渲染逻辑,用flutter_tex替换对应内容。以下是可落地的具体方案:


步骤1:明确Delta中LaTeX的标识方式

首先要确认你的Delta里LaTeX是怎么存储的,常见两种方式:

  • 方式一:自定义属性标记(推荐,准确率高)
    比如导出的Delta会给LaTeX内容加上专属属性:
    {
      "ops": [
        {"insert": "普通文本"},
        {"insert": "$E=mc^2$", "attributes": {"latex": "inline"}},
        {"insert": "\n"},
        {"insert": "$$\\sum_{i=1}^n i = \\frac{n(n+1)}{2}$$", "attributes": {"latex": "block"}}
      ]
    }
    
  • 方式二:文本用$(行内)或$$(块级)包裹
    这种情况需要先通过正则匹配提取LaTeX内容。

步骤2:预处理Delta(针对文本包裹的情况)

如果你的Delta是用$/$$包裹LaTeX,需要先把这些内容转换成带属性的操作,方便后续渲染识别:

import 'package:quill_delta/quill_delta.dart';

List<Operation> processDeltaForLatex(List<Operation> ops) {
  final processedOps = <Operation>[];
  for (final op in ops) {
    if (op.insert is String) {
      final text = op.insert as String;
      // 处理行内公式 $...$
      final inlineRegex = RegExp(r'\$([^\$]+)\$');
      final inlineMatches = inlineRegex.allMatches(text);
      if (inlineMatches.isNotEmpty) {
        int start = 0;
        for (final match in inlineMatches) {
          if (match.start > start) {
            processedOps.add(Operation.insert(text.substring(start, match.start)));
          }
          // 提取LaTeX内容,添加inline属性
          processedOps.add(Operation.insert(
            match.group(1)!,
            attributes: {'latex': 'inline'},
          ));
          start = match.end;
        }
        if (start < text.length) {
          processedOps.add(Operation.insert(text.substring(start)));
        }
        continue;
      }
      // 处理块级公式 $$...$$
      final blockRegex = RegExp(r'\$\$([^\$\$]+)\$\$');
      final blockMatches = blockRegex.allMatches(text);
      if (blockMatches.isNotEmpty) {
        int start = 0;
        for (final match in blockMatches) {
          if (match.start > start) {
            processedOps.add(Operation.insert(text.substring(start, match.start)));
          }
          // 提取LaTeX内容,添加block属性
          processedOps.add(Operation.insert(
            match.group(1)!,
            attributes: {'latex': 'block'},
          ));
          processedOps.add(const Operation.insert('\n'));
          start = match.end;
        }
        if (start < text.length) {
          processedOps.add(Operation.insert(text.substring(start)));
        }
        continue;
      }
      // 无LaTeX的普通文本,直接添加
      processedOps.add(op);
    } else {
      processedOps.add(op);
    }
  }
  return processedOps;
}

步骤3:扩展flutter_quill的自定义渲染器

通过flutter_quill提供的customInlineSpanBuilders(行内内容)和customBlockEmbedBuilders(块级内容),把带latex属性的内容替换成flutter_tex的TeXView组件:

3.1 初始化处理后的QuillController

import 'package:flutter_quill/flutter_quill.dart';

// 假设你的原始Delta JSON
final rawDeltaJson = yourDeltaSource;
final rawDelta = Delta.fromJson(rawDeltaJson);
// 预处理Delta,添加LaTeX属性
final processedDelta = Delta()..addAll(processDeltaForLatex(rawDelta.toList()));
// 创建Quill控制器
final _quillController = QuillController(
  document: Document.fromDelta(processedDelta),
  selection: const TextSelection.collapsed(offset: 0),
);

3.2 配置QuillEditor的自定义渲染

import 'package:flutter_tex/flutter_tex.dart';

QuillEditor(
  controller: _quillController,
  readOnly: true, // 如果是仅展示场景,设置为true
  customInlineSpanBuilders: {
    // 渲染行内LaTeX公式
    'latex': (context, attributes, text) {
      return WidgetSpan(
        alignment: PlaceholderAlignment.middle,
        child: TeXView(
          child: TeXViewDocument(r'$' + text + r'$'),
          style: TeXViewStyle(
            fontSize: 16,
            padding: const EdgeInsets.symmetric(vertical: 2),
          ),
        ),
      );
    },
  },
  customBlockEmbedBuilders: {
    // 渲染块级LaTeX公式
    'latex': (context, node, text) {
      return TeXView(
        child: TeXViewDocument(r'$$' + node.value.data + r'$$'),
        style: TeXViewStyle(
          padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
          backgroundColor: Colors.grey.shade50,
          borderRadius: const BorderRadius.all(Radius.circular(4)),
        ),
      );
    },
  },
)

步骤4:flutter_tex的平台配置

确保在对应平台配置文件中添加必要设置:

  • Android:在android/app/src/main/AndroidManifest.xml的<activity>标签内添加:
    android:hardwareAccelerated="true"
    
  • iOS:在ios/Runner/Info.plist中添加:
    <key>io.flutter.embedded_views_preview</key>
    <string>YES</string>
    

注意事项

  • 如果你的Delta是用自定义BlockEmbed存储LaTeX(比如Quill的公式插件导出的格式),需要对应实现CustomBlockEmbed类来识别,替换上述customBlockEmbedBuilders的逻辑即可。
  • flutter_tex依赖WebView渲染,确保测试设备的WebView版本支持LaTeX渲染(一般现代设备都没问题)。

内容的提问来源于stack exchange,提问作者Sanket More

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:55:57