В предыдущем параграфе мы начали работать с графикой: разобрались, что такое CustomPainter и Canvas и нарисовали с их помощью рисунок.
В этом параграфе мы продолжим работать с графикой: нарисуем на холсте текст с помощью класса TextPainter и изображения.
Текст
Для отрисовки на холсте текста воспользуемся классом TextPainter. Процесс отрисовки можно разбить на три этапа:
- декларация;
- расчёт размеров;
- отрисовка.
Рассмотрим каждый этап подробнее.
Декларация
Есть два параметра, которые обязательно необходимо передать TextPainter, чтобы нарисовать текст, — это text и textDirection:
text— принимает наследниковInlineSpan, т. е.TextSpanиWidgetSpan;textDirection— принимает enumTextDirection, который определяет направление текста.
Необязательные параметры 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}
Далее — отрисуем на холсте картинку.
Изображения
На практике изображения с помощью 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}
Image и параметры Paint
Данные параметры класса Paint используются для модификации изображения.
imageFilter— объект классаImageFilter. Представляет собой набор операций преобразования растрового изображения.filterQuality— enumFilterQuality. Управляет соотношением производительности и качества, используемым при сэмплинге растровых изображений.invertColors— флаг, инвертирует цвета изображения.
Вот и всё! В этом параграфе мы узнали, как добавлять текст и изображения.
В следующем мы продолжим говорить о CustomPainter — разберём продвинутые аспекты: работу со слоями, использование шейдеров, обрезку и трансформацию холста и многое другое.
