введение
Функция обмена приложением обычно включает обмен снимками экрана. Как правило, функция скриншота Flutter выполняется черезRepaintBoundary
Конкретная реализация очень проста и не будет здесь подробно останавливаться. В этой статье в основном речь идет о двух моментах: длинных скриншотах и сшивке изображений.
1. Длинный скриншот
В реальных потребностях область, которая должна быть скриншон, может превышать один экран, то есть длинный скриншот требуется. Эта область обычно вложена в прокручиваемому контейнере, напримерSingleChildScroolView
Ждать.
Если мы обернем контейнер прокрутки напрямуюRepaintBoundary
,Например:
RepaintBoundary(
key: key ,
child: SingleChildScrollView(
child: Column(
children:[
widget1,
widget2,
widget3,
...
]
),
),
);
Когда вы получите снимок экрана, вы обнаружите, что на снимке экрана отображается только та область, которая в данный момент отображается на экране!
Причина на самом деле довольно проста:RepaintBoundary
Будет измерять ширину и высоту прямого дочернего виджета, а в это время его прямого дочернего виджета (в примереSingleChildScrollView
) — максимальная ширина и высота экрана.
Решение тоже очень простое: поставитьRepaintBoundary
Установите на внешний слой, который действительно нужно разрезать, как правилоColumn
(Ширина и высота этого виджета — это ширина и высота, которые мы на самом деле хотим вырезать). Приведенный выше пример будет изменен на:
SingleChildScrollView(
child: RepaintBoundary(
key: key ,
child: Column(
children:[
widget1,
widget2,
widget3,
...
]
),
),
);
Еще одна вещь, которую следует отметить, это то, что если цвет фона страницы установлен наRepaintBoundary
Внешний слой, то снимок экрана имеет прозрачный фон, затем вам нужно установить цвет фона во внутреннем слое.
2. Сшивка картинок
Идеальная ситуация для длинного снимка экрана выше была бы такой:SingleChildScrollView
Это позволяет нам установить слой в дочернемRepaintBoundary
, но на практике может быть и такCustomScrollView
Этот тип строгих ограничений на детей, в это время нам может потребоваться использовать аналогичныйSliverToBoxAdapter
Такой контейнер, и каждый набор на ребенкаRepaintBoundary
,Например:
CustomScrollView(
slivers: <Widget>[
///appbar
SliverAppBar(
flexibleSpace: RepaintBoundary(
key: key,
child: FlexibleSpaceBar(... ),
),
),
///内容
SliverToBoxAdapter(
child: RepaintBoundary(
key: key2,
child:Column(...)
)
),
],
)
Но это становится скриншотом небольшой области. В качестве альтернативы, многие из реальных бизнесов потребуют вставки рекламного контента в нижней части скриншота, например QR-кода.Эти ситуации требуют от нас выполнения сшивания изображений.
Для сшивания изображений моя идея состоит в том, чтобы: использоватьpaint
canvas
- Рассчитайте максимальную ширину / высоту в зависимости от ширины и высоты каждого изображения для разделения
- Если вы хотите поддерживать альбомную и портретную ориентацию, вам также необходимо определить ориентацию
- Ширина и высота изображений могут быть разными, и их необходимо масштабировать, чтобы они были согласованными (ширина вертикального расположения должна быть одинаковой, а высота горизонтального расположения должна быть одинаковой).
Конкретная реализация выглядит следующим образом:
(1)Определить параметры, открытые для внешнего мира.imageList список изображений (обязательно), направление направления, масштабируется ли подгонка, чтобы быть последовательным.
(2)
///计算最大图片宽/高
int maxWidth = 0;
int maxHeight = 0;
imageList.forEach((image) {
if (direction == Axis.vertical) {
if (maxWidth < image.width) maxWidth = image.width;
} else {
if (maxHeight < image.height) maxHeight = image.height;
}
});
///建立变量记录总高及总宽
int totalHeight = maxHeight;
int totalWidth = maxWidth;
(3)Инициализируйте управление краской.
ui.PictureRecorder recorder = ui.PictureRecorder();
final paint = Paint();
Canvas canvas = Canvas(recorder);
(4)Нарисуйте в зависимости от того, подходит он или нет.Если подойдет, он должен быть масштабированcanvas
Для соответствия ширине/высоте и записи точки координат
double dx = 0;
double dy = 0;
//draw images into canvas
imageList.forEach((image) {
double scaleDx = dx;
double scaleDy = dy;
double imageHeight = image.height.toDouble();
double imageWidth = image.width.toDouble();
if (fit) {
//scale the image to same width/height
canvas.save();
if (direction == Axis.vertical && image.width != maxWidth) {
canvas.scale(maxWidth / image.width);
scaleDy *= imageWidth/maxWidth;
imageHeight *= maxWidth/imageWidth;
} else if (direction == Axis.horizontal && image.height != maxHeight) {
canvas.scale(maxHeight / image.height);
scaleDx *= imageHeight/maxHeight;
imageWidth *= maxHeight/imageHeight;
}
canvas.drawImage(image, Offset(scaleDx, scaleDy), paint);
canvas.restore();
} else {
//draw directly
canvas.drawImage(image, Offset(dx, dy), paint);
}
//accumulate dx/dy
if (direction == Axis.vertical) {
dy += imageHeight;
totalHeight += imageHeight.floor();
} else {
dx += imageWidth;
totalWidth += imageWidth.floor();
}
});
(5)выходное изображение.Формат по умолчаниюui.Image
Если нужноUint8List
илиFile
Подождите, вам нужно преобразовать это самостоятельно.
return recorder.endRecording().toImage(totalWidth, totalHeight);
Предварительный просмотр эффекта:
Элементы управления сшиванием изображений были доставлены на рынок пабов:merge_images, который поставляется с некоторым преобразованием формата изображения и функцией прямого использования отображения виджета, вы можете использовать его, если вам это нужно.