如何在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
相关产品推荐
相关产品推荐

