一、flutter_markdown_plus 解析插件详解

当小鱼想去了解 flutter_markdown插件的时候,发现已经有增强版啦:flutter_markdown_plus,就一起学习一下吧。

1、官网

https://pub.dev/packages/flutter_markdown_plus

2、架构

输入Markdown文本
    ↓
语法解析(markdown → AST)
    ↓
AST转换(自定义节点处理)
    ↓
Widget树构建
    ↓
Flutter渲染

3、核心流程解析

1)Markdown → AST 解析

// 使用扩展的解析器
final markdown = Markdown( 
  extensionSet: ExtensionSet.gitHubFlavoredWithPlus,
  inlineSyntaxes: [...customSyntaxes],
  blockSyntaxes: [...customSyntaxes],
);

// 解析为抽象语法树
final document = markdown.parse(markdownText);

没想到吧,markdow UI是ListView实现的!

2)AST → Widget 转换

// 1、节点映射
class MarkdownBuilderPlus extends MarkdownBuilder {
  @override
  Widget visitElementAfter(Element element, TextStyle? preferredStyle) {
    // 自定义节点处理
    switch (element.tag) {
      case 'custom-table':
        return _buildCustomTable(element);
      case 'math':
        return _buildMathFormula(element);
      case 'alert':
        return _buildAlertBox(element);
    }
    
    // 默认处理
    return super.visitElementAfter(element, preferredStyle);
  }
}

// 2、自定义渲染器
class MarkdownPlus extends MarkdownWidget {
  const MarkdownPlus({
    required String data,
    MarkdownStyleSheet? styleSheet,
    SyntaxHighlighter? syntaxHighlighter,
  }) : super(
          data: data,
          styleSheet: styleSheet,
          syntaxHighlighter: syntaxHighlighter,
          builders: {
            'table': CustomTableBuilder(),
            'math': KatexBuilder(),
            'emoji': EmojiBuilder(),
          },
          extensionSet: _createExtensionSet(),
        );
}

3)、表格特性增强

class EnhancedTableBuilder extends MarkdownElementBuilder {
  @override
  Widget visitElementAfter(Element element, _) {
    // 解析表格数据
    final tableData = _parseTable(element);
    
    return Table(
      border: TableBorder.all(color: Colors.grey[300]!),
      children: tableData.rows.map((row) {
        return TableRow(
          children: row.cells.map((cell) {
            return Container(
              padding: EdgeInsets.all(8),
              child: MarkdownBody(data: cell.content),
            );
          }).toList(),
        );
      }).toList(),
    );
  }
}

4)、数学公式增强

// 集成 KaTeX 或 MathJax
class MathBuilder implements MarkdownElementBuilder {
  @override
  Widget visitElementAfter(Element element, _) {
    final tex = element.textContent;
    return FutureBuilder(
      future: _renderMath(tex), // 调用JS引擎或原生库
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return CustomPaint(painter: MathPainter(snapshot.data!));
        }
        return CircularProgressIndicator();
      },
    );
  }
}

5)、自定义容器

class AlertBoxBuilder extends MarkdownElementBuilder {
  final Map<String, Color> alertTypes = {
    'info': Colors.blue,
    'warning': Colors.orange,
    'danger': Colors.red,
  };
  
  @override
  Widget visitElementAfter(Element element, _) {
    final type = element.attributes['type'] ?? 'info';
    return Container(
      decoration: BoxDecoration(
        color: alertTypes[type]!.withOpacity(0.1),
        border: BorderLeft: BorderSide(color: alertTypes[type]!, width: 4),
      ),
      padding: EdgeInsets.all(12),
      child: MarkdownBody(data: element.textContent),
    );
  }
}

4、性能优化策略

1)、懒加载与缓存

class CachedMarkdown extends StatefulWidget {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<Widget>(
      future: _cachedMarkdown(), // 缓存解析结果
      builder: (context, snapshot) {
        return snapshot.data ?? Placeholder();
      },
    );
  }
}

2)增量更新

// 使用 KeyedSubtree 实现局部更新
class IncrementalMarkdown extends StatelessWidget {
  final String markdown;
  final String previousMarkdown;
  
  @override
  Widget build(BuildContext context) {
    // 对比差异,只更新变化的部分
    final diff = _calculateDiff(previousMarkdown, markdown);
    
    return KeyedSubtree(
      key: ValueKey(diff.changedBlocks),
      child: MarkdownPlus(data: markdown),
    );
  }
}

5、扩展机制

1)自定义语法注册

extensionSet = ExtensionSet.gitHubFlavored.addAll([
  // 自定义语法扩展
  CustomBlockSyntax(),
  CustomInlineSyntax(),
  
  // 渲染器扩展
  CustomHtmlTagExtension(
    tags: ['video', 'audio', 'chart'],
    builder: CustomMediaBuilder(),
  ),
]);

2)插件系统

class MarkdownPlusPlugin {
  final List<MarkdownElementBuilder> builders;
  final List<BlockSyntax> blockSyntaxes;
  final List<InlineSyntax> inlineSyntaxes;
  
  void register(MarkdownPlusController controller) {
    controller.registerBuilders(builders);
    controller.registerSyntaxes(blockSyntaxes, inlineSyntaxes);
  }
}

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐