import 'package:flutter/material.dart'; import 'package:remever/common/resources.dart'; class MarkdownText extends StatelessWidget { final String text; final TextStyle? baseStyle; const MarkdownText(this.text, {super.key, this.baseStyle}); @override Widget build(BuildContext context) { return SelectableText.rich( TextSpan( children: _parseMarkdown(text), style: baseStyle ?? const TextStyle(fontSize: 14, color: AppColors.body_text), ), ); } List _parseMarkdown(String text) { final lines = text.split('\n'); final spans = []; for (var line in lines) { final trimmed = line.trim(); // H1 if (trimmed.startsWith('# ') && trimmed.length > 2) { spans.add( TextSpan( text: '${trimmed.substring(2)}\n', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ); continue; } // Нумерованный список if (RegExp(r'^\d+\.\s').hasMatch(trimmed)) { final match = RegExp(r'^(\d+)\.\s(.+)').firstMatch(trimmed); if (match != null) { spans.add( TextSpan( text: '${match.group(0)!}\n', style: const TextStyle(fontSize: 14), ), ); continue; } } // Маркированный список if (trimmed.startsWith('• ') && trimmed.length > 2) { spans.add( TextSpan( text: '${trimmed.substring(2)}\n', style: const TextStyle(fontSize: 14), ), ); continue; } // Жирный текст final boldRegex = RegExp(r'\*\*([^*]+)\*\*'); var remaining = line; var boldProcessed = []; int lastEnd = 0; for (final match in boldRegex.allMatches(line)) { if (match.start > lastEnd) { boldProcessed.add( TextSpan(text: line.substring(lastEnd, match.start)), ); } boldProcessed.add( TextSpan( text: match.group(1), style: const TextStyle(fontWeight: FontWeight.bold), ), ); lastEnd = match.end; } if (lastEnd < line.length) { boldProcessed.add(TextSpan(text: line.substring(lastEnd))); } boldProcessed.add(const TextSpan(text: '\n')); spans.addAll(boldProcessed); } return spans; } }