Files
be_happy_public/lib/presentation/components/content_parser.dart

244 lines
6.6 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import 'package:html/parser.dart' as html_parser;
import 'package:html/dom.dart' as dom;
import 'dart:convert';
/// Парсер контента новостей (поддерживает HTML и JSON-формат)
class ContentParser {
/// Парсит текст новости: сначала проверяет textJson, потом text
static List<Widget> parseContent({String? textJson, String? text}) {
if (textJson != null && textJson.isNotEmpty) {
return _parseJsonText(textJson);
} else if (text != null && text.isNotEmpty) {
return _parseHtmlText(text);
}
return const [];
}
// 🔹 HTML-парсинг
static List<Widget> _parseHtmlText(String htmlText) {
final parsedHtml = html_parser.parse(htmlText);
final List<dom.Node> elements = parsedHtml.body?.nodes ?? [];
return _parseHtmlElements(elements);
}
static List<Widget> _parseHtmlElements(List<dom.Node> nodes) {
List<Widget> widgets = [];
for (final node in nodes) {
if (node is dom.Element) {
final text = node.text.trim();
if (text.isEmpty) continue;
switch (node.localName) {
case 'h1':
case 'h2':
case 'h3':
widgets.add(_buildHeader(text));
break;
case 'p':
widgets.add(_buildParagraph(text));
break;
case 'ul':
widgets.add(_buildUnorderedList(node.nodes));
break;
default:
widgets.add(_buildDefaultText(text));
}
} else if (node is dom.Text) {
final text = node.text.trim();
if (text.isNotEmpty) {
widgets.add(_buildDefaultText(text));
}
}
}
return widgets;
}
// 🔹 JSON-парсинг
static List<Widget> _parseJsonText(String jsonString) {
try {
final dynamic data = jsonDecode(jsonString);
return _parseJsonNode(data);
} catch (e) {
return [
const Text(
'Ошибка отображения текста новости',
style: TextStyle(color: Colors.red, fontSize: 14),
),
];
}
}
static List<Widget> _parseJsonNode(dynamic node) {
List<Widget> widgets = [];
if (node is List) {
for (final item in node) {
widgets.addAll(_parseJsonNode(item));
}
} else if (node is Map<String, dynamic>) {
final type = node['type'] as String?;
final content = node['content'];
switch (type) {
case 'div':
if (content is List) {
widgets.addAll(_parseJsonNode(content));
}
break;
case 'h1':
case 'h2':
case 'h3':
final text = _extractJsonText(content);
if (text != null && text.isNotEmpty) {
widgets.add(_buildHeader(text));
}
break;
case 'p':
final text = _extractJsonText(content);
if (text != null && text.isNotEmpty) {
widgets.add(_buildParagraph(text));
}
break;
case 'ul':
if (content is List) {
widgets.add(_buildJsonUnorderedList(content));
}
break;
case 'li':
final text = _extractJsonText(content);
if (text != null && text.isNotEmpty) {
widgets.add(_buildListItem(text));
}
break;
default:
final text = _extractJsonText(content);
if (text != null && text.isNotEmpty) {
widgets.add(_buildDefaultText(text));
}
}
}
return widgets;
}
// 🔹 Вспомогательные методы извлечения текста из JSON
static String? _extractJsonText(dynamic content) {
if (content == null) return null;
if (content is List) {
return content.map((e) => e?.toString() ?? '').where((s) => s.isNotEmpty).join(' ');
}
return content.toString();
}
// 🔹 Виджеты для отображения (принимают НЕ nullable String)
static Widget _buildHeader(String text) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: Text(
text, // ✅ text — не nullable
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
);
}
static Widget _buildParagraph(String text) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Text(
text,
style: const TextStyle(
color: Colors.white70,
fontSize: 14,
height: 1.5,
),
),
);
}
static Widget _buildDefaultText(String text) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Text(
text,
style: const TextStyle(
color: Colors.white70,
fontSize: 14,
),
),
);
}
static Widget _buildUnorderedList(List<dom.Node> children) {
final items = <Widget>[];
for (final child in children) {
if (child is dom.Element && child.localName == 'li') {
final text = child.text.trim();
if (text.isNotEmpty) {
items.add(_buildListItem(text));
}
}
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: items,
),
);
}
static Widget _buildJsonUnorderedList(List<dynamic> content) {
final items = <Widget>[];
for (final item in content) {
if (item is Map<String, dynamic> && item['type'] == 'li') {
final text = _extractJsonText(item['content']);
if (text != null && text.isNotEmpty) {
items.add(_buildListItem(text));
}
}
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: items,
),
);
}
static Widget _buildListItem(String text) {
return Padding(
padding: const EdgeInsets.only(left: 16, top: 4, bottom: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('',
style: TextStyle(
color: Colors.white70,
fontSize: 14,
)),
Expanded(
child: Text(
text,
style: const TextStyle(
color: Colors.white70,
fontSize: 14,
height: 1.5,
),
),
),
],
),
);
}
}