Como pintar en Flutter
Una guía para aprender a usar el widget CustoPaint
.
Hola amigos, después de un largo tiempo de no hacer una traducción, les traigo el tema del widget CustomPaint publicada por Suragch. El artículo original en ingles lo puedes encontrar en el siguiente enlace: How to paint in Flutter.
Si no lo has visto ya, tu podrías querer empezar por ver el video Widget Flutter de la semana acerca del CustomPaint
. Estaré mostrando como hacer muchas de las cosas de ese video.
Preparar
Crear un proyecto nuevo y remplace el main.dart con el siguiente código:
Notas:
- Para pintar en Flutter use el widget
CustomPaint
. Si no le das un widget hijo, deberías establecer un tamaño. Aquí hice el tamaño300 x 300
pixeles lógicos. Si le diste un widget hijo, entonces CustomPaint tomará su tamaño. Elpainter
pintará debajo del widget hijo yforegroundPainter
pintará en la parte superior del widget hijo. - El widget
CustomPaint
toma un objetoCustomPainter
(observe la terminación -er) como un parámetro. ElCustomPainter
te da un lienzo en el que puedes pintar. - La subclase
CustomPainter
anula dos métodos:paint()
yshouldRepaint()
. - Harás tu pintura personalizada en
paint()
. Para todos mis ejemplos a continuación, inserté el código aquí. shouldRepaint()
es llamado cuando elCustomPainter
es reconstruido. Si retornasfalse
el framework usará el resultado anterior de pintura (así ahorrando trabajo). Pero si retornastrue
entoncespaint()
será llamado de nuevo. Podrías hacer algo como comprobar sioldPainter.someParameter != someParameter
para tomar la decisión. Pero hoy no tenemos parámetros cambiantes así que retornarásfalse
.
Puntos
Agrega la siguiente importación:
import 'dart:ui' as ui;
Reemplace MyPainter.paint()
con el siguiente código:
@override
void paint(Canvas canvas, Size size) {
final pointMode = ui.PointMode.points;
final points = [
Offset(50, 100),
Offset(150, 75),
Offset(250, 250),
Offset(130, 200),
Offset(270, 100),
];
final paint = Paint()
..color = Colors.black
..strokeWidth = 4
..strokeCap = StrokeCap.round;
canvas.drawPoints(pointMode, points, paint);
}
Notas:
- Debes mantenerte dentro de los límites del
size
. - Un
Offset
es un par de doubles(dx,dy)
, el offset de la esquina superior izquierda es(0,0)
. - Si no configuras el color, por defecto es blanco.
Lineas
Reemplazar MyPainter.paint()
con el siguiente código:
@override
void paint(Canvas canvas, Size size) {
final p1 = Offset(50, 50);
final p2 = Offset(250, 150);
final paint = Paint()
..color = Colors.black
..strokeWidth = 4;
canvas.drawLine(p1, p2, paint);
}
Notas:
- El método
drawLine
dibuja una línea conectando los dos puntos que le das. Podrías hacer lo mismo condrawPoints
usando las opcionesPointMode.lines
orPointMode.polygon
.
Rectángulos
Reemplazar MyPainter.paint()
con el siguiente código:
@override
void paint(Canvas canvas, Size size) {
final left = 50.0;
final top = 100.0;
final right = 250.0;
final bottom = 200.0;
final rect = Rect.fromLTRB(left, top, right, bottom);
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 4;
canvas.drawRect(rect, paint);
}
- Usando
PaintingStyle.stroke
hace el rectángulo delineado.Si lo quisieras relleno podrías usarPaintingStyle.fill
.
Círculos
Reemplazar MyPainter.paint()
con el siguiente código:
@override
void paint(Canvas canvas, Size size) {
final center = Offset(150, 150);
final radius = 100.0;
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 4;
canvas.drawCircle(center, radius, paint);
}
Óvalos
Reemplazar MyPainter.paint()
con el siguiente código:
@override
void paint(Canvas canvas, Size size) {
final rect = Rect.fromLTRB(50, 100, 250, 200);
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 4;
canvas.drawOval(rect, paint);
}
Notas:
LTRB
significa izquierda, arriba, derecha, abajo.
Arcos
Agregar la siguiente importación:
import 'dart:math' as math;
Reemplazar MyPainter.paint()
con el siguiente código:
@override
void paint(Canvas canvas, Size size) {
final rect = Rect.fromLTRB(50, 100, 250, 200);
final startAngle = -math.pi / 2;
final sweepAngle = math.pi;
final useCenter = false;
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 4;
canvas.drawArc(rect, startAngle, sweepAngle, useCenter, paint);
}
Notas:
- El
rect
es lo que estaría inscrito, del óvalo completo. - El
startAngle
es la ubicación en el óvalo, donde comienza a dibujarse la línea. Un ángulo de0
está en el lado derecho. Los ángulos están en radianes, no en grados. La parte superior está en 3π / 2 (o -π / 2), la izquierda en π y la parte inferior en π / 2. - El
sweepAngle
es la parte del óvalo que se incluye en el arco. Nuevamente, los ángulos están en radianes. Un valor de 2π dibujaría todo el óvalo. - Si estableces
useCenter
atrue
, entonces habrá una línea recta de ambos lados del arco hacia el centro. Se verá como un trozo de pastel.
Rutas
Reemplazar MyPainter.paint()
con el siguiente código:
@override
void paint(Canvas canvas, Size size) {
final path = Path()
..moveTo(50, 50)
..lineTo(200, 200)
..quadraticBezierTo(200, 0, 150, 100);
final paint = Paint()
..color = Colors.black
..style = PaintingStyle.stroke
..strokeWidth = 4;
canvas.drawPath(path, paint);
}
Notas:
moveTo
va a donde comienza la ruta.lineTo
dibuja una línea desde la ubicación actual en la ruta hacia las coordenadas(x,y)
dadas.- Los primeros dos argumentos del método
quadraticBezierTo
son los valores (x, y) del punto de control. Los dos últimos argumentos son los valores (x,y) de donde termina la curva de Bezier. - Hay muchas más opciones para hacer rutas, por lo que tendrás que esperar a otra lección.
Texto
Versión de bajo nivel
import 'dart:ui' as ui;
Reemplazar MyPainter.paint()
con el siguiente código:
@override
void paint(Canvas canvas, Size size) {
final textStyle = ui.TextStyle(
color: Colors.black,
fontSize: 30,
);
final paragraphStyle = ui.ParagraphStyle(
textDirection: TextDirection.ltr,
);
final paragraphBuilder = ui.ParagraphBuilder(paragraphStyle)
..pushStyle(textStyle)
..addText('Hello, world.');
final constraints = ui.ParagraphConstraints(width: 300);
final paragraph = paragraphBuilder.build();
paragraph.layout(constraints);
final offset = Offset(50, 100);
canvas.drawParagraph(paragraph, offset);
}
Notas:
- Cuando usas métodos de bajo nivel desde
dart:ui
es costumbre prefijar las clases conui
. Esto además te ayuda con los conflictos de nombres. Por ejemplo,TextStyle
está también definido en la biblioteca painting. Si usaste eseTextStyle
, entonces necesitarías codificarlo paradart:ui
conTextStyle().getTextStyle()
para un solo estilo oTextStyle().build()
para aplicar el árbol de estilos recursivamente. - Si tú proyecto tiene un fondo blanco asegúrese de configurar el color de estilo del texto a negro o algo que puedas ver. El color de texto por defecto es blanco.
ltr
significa izquierda a derecha.- Un
ParagraphBuilder
es usado para construir unParagraph
, que elCanvas
usa para dibujar el texto. Le das estilo al texto presionando y haciendo estallar el estilo de texto a medida que agrega cadenas de texto. - Antes de que puedas pintar el texto, tienes que diseñarlo. Esta tarea es pasada al motor de Skia.
Versión de alto nivel
No es necesario importar dart:ui
. Reemplazar MyPainter.paint()
con el siguiente código:
@override
void paint(Canvas canvas, Size size) {
final textStyle = TextStyle(
color: Colors.black,
fontSize: 30,
);
final textSpan = TextSpan(
text: 'Hello, world.',
style: textStyle,
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout(
minWidth: 0,
maxWidth: size.width,
);
final offset = Offset(50, 100);
textPainter.paint(canvas, offset);
}
Notas:
TextPainter
internamente llama acanvas.drawParagraph
.- Flutter hace un esfuerzo por no asumir una dirección de texto, por lo que debes establecerla explícitamente. La abreviación
ltr
significa de izquierda a derecha, que utilizan idiomas como Inglés. La otra opción esrtl
(right-to-left), que utilizan idiomas como Árabe o Hebreo. Esto ayuda a reducir errores cuando el código es usado en contextos de lenguaje en los que los desarrolladores no estaban pensado. - Incluso con esta versión de alto nivel, todavía tienes que diseñar el texto antes de pintarlo.
Continuando
Hay más cosas que puedes dibujar de las que cubrí aquí. Aquí hay algunos más para ver:
drawImage
drawImageNine
(parche nueve)drawShadow
Puedes aprender más en estos recursos: