Como pintar en Flutter

Eduardo CQ
7 min readOct 13, 2020

--

Una guía para aprender a usar el widget CustoPaint .

Photo by Steve Johnson

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ño 300 x 300 pixeles lógicos. Si le diste un widget hijo, entonces CustomPaint tomará su tamaño. El painter pintará debajo del widget hijo y foregroundPainter pintará en la parte superior del widget hijo.
  • El widget CustomPaint toma un objeto CustomPainter (observe la terminación -er) como un parámetro. El CustomPainter te da un lienzo en el que puedes pintar.
  • La subclase CustomPainter anula dos métodos: paint() y shouldRepaint().
  • Harás tu pintura personalizada en paint(). Para todos mis ejemplos a continuación, inserté el código aquí.
  • shouldRepaint() es llamado cuando el CustomPainter es reconstruido. Si retornas false el framework usará el resultado anterior de pintura (así ahorrando trabajo). Pero si retornas true entonces paint()será llamado de nuevo. Podrías hacer algo como comprobar si oldPainter.someParameter != someParameter para tomar la decisión. Pero hoy no tenemos parámetros cambiantes así que retornarás false.

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 con drawPoints usando las opciones PointMode.lines or PointMode.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 usar PaintingStyle.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 de 0 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 a true, 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 con ui. Esto además te ayuda con los conflictos de nombres. Por ejemplo, TextStyle está también definido en la biblioteca painting. Si usaste ese TextStyle, entonces necesitarías codificarlo para dart:ui con TextStyle().getTextStyle() para un solo estilo o TextStyle().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 un Paragraph, que el Canvas 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 a canvas.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 es rtl(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:

Les dejo el código fuente, que abarca los ejercicios vistos en este artículo: github

Gracias por leer este artículo ❤
Si este artículo te gusta , aplauda tantas veces como pueda para mostrar su apoyo. 👏
Puedes encontrarme en GitHub
Sígueme en Twitter.

Leer algunos de mis artículos y traducciones.

--

--