Delta Text View

Версия: 1.0.0

Пакет для преобразования Delta формата (используется в Quill редакторе) в Flutter виджеты.

Описание

delta_text_view рендерит Quill Delta-документы в Flutter. Блоковые атрибуты (заголовки, списки, цитаты, код) отображаются как текстовые префиксы (# , - , 1. , > ). Поддерживаются произвольные embed-объекты — упоминания пользователей и emoji — через конфигурацию MentionConfig/EmojiConfig, без привязки к конкретной модели данных.

Зависимости

  • dart_quill_delta — модель Delta (реэкспортируется пакетом)
  • flutter — виджеты
  • url_launcher — открытие ссылок по умолчанию

Публичное API

DeltaTextView

Основной виджет для отображения Delta.

Параметры:

  • delta (обязательный) — документ Delta
  • defaultStyle (обязательный) — базовый TextStyle
  • mentionConfig — конфигурация для разбора и отображения упоминаний (null, если упоминания не нужны)
  • emojiConfig — конфигурация для разбора emoji-эмбедов (по умолчанию EmojiConfig(), null отключает распознавание)
  • emojiOnlySize — размер emoji, когда Delta состоит только из emoji-эмбедов (по умолчанию 48, null отключает автоувеличение)
  • maxLines — ограничение по количеству строк
  • textAlign — выравнивание текста
  • overflow — поведение при переполнении; можно задавать только когда selectable == null
  • selectabletrue включает выделение текста (SelectableDeltaTextView), false/null — обычный Text.rich; overflow разрешён только при null
  • selectionColor — цвет выделения
  • onSelectionChangedAsDelta — callback с под-Delta выделенного диапазона (или null, если выделение снято)
  • onTapSelectableText — callback нажатия по выделяемому тексту
  • onLinkTap — обработчик нажатия на ссылку; по умолчанию открывает url_launcher, передайте null чтобы отключить
  • contextMenuBuilder — переопределение контекстного меню выделения

Пример:

import 'package:delta_text_view/delta_text_view.dart';

DeltaTextView(
  delta: delta,
  defaultStyle: const TextStyle(fontSize: 16),
  mentionConfig: mentionConfig,
)

MentionConfig

Конфигурация разбора и отображения упоминаний произвольного типа.

  • embedKey — ключ embed-объекта в Delta (по умолчанию 'mention')
  • fromJson — десериализация модели из JSON внутри embed-объекта; верните null, если формат не подходит
  • widgetBuilder — виджет для отображения упоминания
  • onTap — callback нажатия на упоминание
final mentionConfig = MentionConfig(
  fromJson: UserMention.fromJson,
  widgetBuilder: (mention) {
    final m = mention as UserMention;
    return Text('@${m.name}', style: const TextStyle(color: Colors.blue));
  },
  onTap: ({required mention, required details}) {
    final m = mention as UserMention;
    print('Нажато упоминание: ${m.id}');
  },
);

Модель упоминания реализует интерфейс MentionDelta:

class UserMention implements MentionDelta {
  final String id;
  final String name;

  UserMention({required this.id, required this.name});

  @override
  String get displayData => name;

  @override
  Map<String, dynamic> toJson() => {'id': id, 'name': name};

  static UserMention? fromJson(Map<String, dynamic> json) {
    final id = json['id'] as String?;
    final name = json['name'] as String?;
    if (id == null || name == null) return null;
    return UserMention(id: id, name: name);
  }
}

EmojiConfig

Конфигурация разбора и отображения emoji-эмбедов.

  • embedKey — ключ embed-объекта в Delta (по умолчанию 'emoji')
  • widgetBuilder — виджет для отображения emoji; если не передан, используется встроенный DefaultEmojiWidget, корректно рендерящий цветные emoji на всех платформах

Поддерживаемые атрибуты

Inline форматирование

  • bold — жирный текст
  • italic — курсив
  • underline — подчёркивание
  • strike — зачёркивание
  • color — цвет текста (формат: #RRGGBB или #RGB)
  • background — цвет фона (формат: #RRGGBB или #RGB)
  • font — семейство шрифта
  • size — размер шрифта (число или строка: small, large, huge)
  • link — ссылка (открывается через onLinkTap)

Block форматирование (текстовые префиксы)

  • header — заголовки (уровни 1-6) → # Текст, ## Текст и т.д.
  • list — списки (bullet/ordered) → - Текст или 1. Текст
  • blockquote — цитаты → > Текст
  • code-block — блоки кода → ```\nТекст
  • indent — отступы (добавляются пробелы)

Embed объекты

  • mention — упоминание пользователя (ключ настраивается через MentionConfig.embedKey):
    {
      "insert": {
        "mention": { "id": "user123", "name": "Иван Иванов" }
      }
    }
    
  • emoji — emoji-эмбед (ключ настраивается через EmojiConfig.embedKey):
    { "insert": { "emoji": "😀" } }
    

Работа с Delta без виджетов

DeltaExtensions (методы на Delta) и String.toDelta покрывают частые операции без рендеринга:

  • delta.toPlainText / delta.toPlainTextWithBlockPrefixes — конвертация в текст (embed-объекты → пробел)
  • delta.documentLength / delta.plainTextLength / delta.lineCount — длины по спецификации Quill Delta
  • delta.isPlainTextEmpty / delta.isPlainTextNotEmpty
  • delta.truncateToLines(n) — обрезка до n строк
  • delta.hasMention(userId) — проверка упоминания пользователя (с поддержкой @all)
  • delta.getMentions(mentionConfig) — список упоминаний, десериализованных через MentionConfig.fromJson
  • delta.stringify — сериализация в JSON-строку
  • '...'.toDelta — строка (JSON или plain text) → Delta

DeltaParser.exceedsMaxLines(...) проверяет, помещается ли Delta в заданное число визуальных строк при заданной ширине — используется, например, для решения о показе кнопки «Развернуть».

Примеры использования

Базовое использование

import 'package:dart_quill_delta/dart_quill_delta.dart';
import 'package:delta_text_view/delta_text_view.dart';
import 'package:flutter/material.dart';

class MessageWidget extends StatelessWidget {
  final Delta delta;

  const MessageWidget({required this.delta});

  @override
  Widget build(BuildContext context) {
    return DeltaTextView(
      delta: delta,
      defaultStyle: const TextStyle(fontSize: 16),
      mentionConfig: null,
    );
  }
}

С обработкой упоминаний

DeltaTextView(
  delta: delta,
  defaultStyle: const TextStyle(fontSize: 16),
  mentionConfig: MentionConfig(
    fromJson: UserMention.fromJson,
    widgetBuilder: (mention) => Text('@${(mention as UserMention).name}'),
    onTap: ({required mention, required details}) {
      final m = mention as UserMention;
      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => UserProfilePage(userId: m.id)),
      );
    },
  ),
)

С ограничением количества строк

DeltaTextView(
  delta: delta,
  defaultStyle: const TextStyle(fontSize: 16),
  mentionConfig: null,
  maxLines: 3,
  overflow: TextOverflow.ellipsis,
)

С выделением текста

DeltaTextView(
  delta: delta,
  defaultStyle: const TextStyle(fontSize: 16),
  mentionConfig: null,
  selectable: true,
  onSelectionChangedAsDelta: (selectionDelta) {
    // например, для копирования с сохранением форматирования
  },
)

Структура пакета

delta_text_view/
├── lib/
│   ├── delta_text_view.dart                        # Главный файл экспорта
│   ├── core/
│   │   ├── delta_parser.dart                     # Delta -> InlineSpan/текст
│   │   ├── delta_attributes_parser.dart          # Атрибуты Delta -> TextStyle
│   │   ├── delta_clipboard.dart                  # Внутрипроцессный буфер обмена для Delta
│   │   └── selection_to_delta_converter.dart     # TextSelection -> под-Delta
│   ├── domain/
│   │   ├── extensions/                           # Delta/String extensions
│   │   └── models/                               # MentionDelta, MentionConfig, EmojiConfig
│   └── presentation/widgets/
│       ├── delta_text_view_widget.dart            # DeltaTextView
│       ├── selectable_delta_text_view.dart            # Выделяемый текст
│       ├── mention_span.dart / mention_widget.dart
│       └── emoji_span.dart / emoji_widget.dart
├── example/
│   ├── lib/main.dart                             # Демонстрационное приложение
│   └── pubspec.yaml
├── pubspec.yaml
└── README.md

Запуск примера

cd example
flutter pub get
flutter run -d linux

Примечания

  • Всё, что не экспортируется из lib/delta_text_view.dart, — внутренняя реализация; импортируйте только package:delta_text_view/delta_text_view.dart
  • Пакет не публикуется в pub.dev (publish_to: none), подключается через git/path