3.9 CustomPainter: работа с текстом и изображениями

В предыдущем параграфе мы начали работать с графикой: разобрались, что такое CustomPainter и Canvas и нарисовали с их помощью рисунок.

В этом параграфе мы продолжим работать с графикой: нарисуем на холсте текст с помощью класса TextPainter и изображения.

Текст

Для отрисовки на холсте текста воспользуемся классом TextPainter. Процесс отрисовки можно разбить на три этапа:

  • декларация;
  • расчёт размеров;
  • отрисовка.

Рассмотрим каждый этап подробнее.

Декларация

Есть два параметра, которые обязательно необходимо передать TextPainter, чтобы нарисовать текст, — это text и textDirection:

  • text — принимает наследников InlineSpan, т. е. TextSpan и WidgetSpan;
  • textDirection — принимает enum TextDirection, который определяет направление текста.

Необязательные параметры TextDirection, скорее всего, будут знакомы читателю по виджетам Text и RichText, поэтому подробно рассматривать их смысла нет.

Расчёт размеров

Следующим этапом является вызов метода layout. Принимает два опциональных параметра:

  • minWidth — минимальная ширина, которая выделяется тексту для расположения. По умолчанию 0.0.

Если вы хотите поменять выравнивание для однострочного текста, необходимо передать минимальную ширину больше ширины строки. Например, size.width холста.

  • maxWidth — максимальная ширина которая выделяется тексту для расположения. По умолчанию double.infinity.

Чтобы текст «перетекал» на следующую строку, необходимо ограничить максимальную ширину. Например, передать size.width холста.

С помощью этого метода рассчитывается положение символов с учётом стиля текста, других переданных параметров в размерах, установленных через minWidth и maxWidth. После вызова метода layout можно обратиться к полю size, чтобы получить итоговые размеры переданного текста.

Отрисовка

Последний этап — вызов метода paint . Принимает два параметра:

  • canvas — объект Canvas холст, на котором будет отрисован текст.
  • offset — объект Offset для позиционирования текста на холсте. Задаёт смещение текста по координатам холста относительно верхнего левого угла.
Код
1class TextCustomPainter  extends CustomPainter {
2  const TextCustomPainter ();
3
4  @override
5  void paint(Canvas canvas, Size size) {
6    // Координата Y середины холста
7    final verticalCenter = size.height / 2;
8    // Смещение
9    final offset = Offset(0, verticalCenter);
10
11    TextPainter(
12      text: const TextSpan(
13        text: 'Текст написан с помощью CustomPainter',
14        style: TextStyle(color: Colors.black),
15      ),
16      textDirection: TextDirection.ltr,
17      textAlign: TextAlign.center,
18    )
19      // Расчёт размеров, который занимает текст
20      ..layout(minWidth: size.width)
21      // Отрисовка текста на холсте
22      ..paint(
23        canvas,
24        offset,
25      );
26  }
27
28  @override
29  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
30}

Group

Далее — отрисуем на холсте картинку.

Изображения

На практике изображения с помощью Canvas практически никогда не отрисовываются —это нетривиальная задача, которая требует много императивных операций для получения изображения в нужном формате. Также многие операции (масштабирование или подстраивание изображения под доступные размеры) будут требовать собственной реализации от разработчика.

Для работы с изображениями лучше использовать виджет Image, который предлагает удобный декларативный подход и покрывает большую часть кейсов использования изображения.

Но раз Canvas поддерживает отрисовку изображений — мы не можем о ней не рассказать.

Итак, с помощью Canvas можно отрисовывать в том числе и растровые изображения. Он поддерживает следующие форматы: JPEG, PNG, GIF, Animated GIF, WebP, Animated WebP, BMP, WBMP.

Работа с изображениями делится на два этапа: загрузку и отрисовку. Расскажем о них подробнее.

Загрузка

Так как загрузка изображения — это асинхронный процесс, получить изображение в методе paint не удастся, и придётся передать его в параметрах конструктора CustomPainter. Это накладывает определённые неудобства и ограничения.

Например, процесс получения изображения требует выполнения ряда низкоуровневых операций.

Код
1Future<ui.Image> _loadImage(String imageAssetPath) async {
2 // Загружаем из постоянной памяти изображение в виде byte-данных
3  final ByteData data = await rootBundle.load(imageAssetPath);
4  
5 // Преобразуем byte-данные в объект Codec
6  final codec = await ui.instantiateImageCodec(
7    data.buffer.asUint8List(),
8  // Если указать желаемый размер только по одной оси —
9  // изображение сохранит оригинальное соотношение сторон
10    targetHeight: 300,
11  );
12
13 // Извлекаем следующий кадр анимации
14  final frame = await codec.getNextFrame();
15 // Возвращаем объект Image для этого кадра
16  return frame.image;
17}

Мы можем указать размеры изображения только заранее (либо не указывать и изображение будет получено в оригинальном размере). Для того чтобы изменить размеры изображения, например, под размер холста, потребуется использовать трансформацию через объект Paint — об этом расскажем далее.

Отрисовка

Для отрисовки изображения на холсте используется метод drawImage. Он принимает параметры:

  • image — объект класса Image из библиотеки dart.ui.
  • offset — объект класса Offset. Смещение левого верхнего угла изображения относительно системы координат холста.
  • paint — объект класса Paint.
Код
1class ImagePainter extends CustomPainter {
2  final ui.Image image;
3
4  ImagePainter(this.image);
5
6  @override
7  void paint(Canvas canvas, Size size) {
8    canvas.drawImage(image, const Offset(0, 0), Paint());
9  }
10
11  @override
12  bool shouldRepaint(ImagePainter oldDelegate) {
13    return false;
14  }
15}

Group

Image и параметры Paint

Данные параметры класса Paint используются для модификации изображения.

  • imageFilter — объект класса ImageFilter. Представляет собой набор операций преобразования растрового изображения.
  • filterQuality — enum FilterQuality. Управляет соотношением производительности и качества, используемым при сэмплинге растровых изображений.
  • invertColors — флаг, инвертирует цвета изображения.

Вот и всё! В этом параграфе мы узнали, как добавлять текст и изображения.

В следующем мы продолжим говорить о CustomPainter — разберём продвинутые аспекты: работу со слоями, использование шейдеров, обрезку и трансформацию холста и многое другое.

Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E
Предыдущий параграф3.8. CustomPainter: работа с графикой
Следующий параграф3.10. CustomPainter: визуальные эффекты