Luís Gustavo
Posted on January 10, 2024
Recentemente a @fernandakipper fez uma live em seu canal, onde ela e o DevSoutinho fizeram uma batalha de CSS. Essa batalha tinha com o objetivo desenhar bandeiras dos países utilizando apenas CSS. Resolvi entrar na brincadeira e fazer esse desafio utilizando o CustomPainter do Flutter.
A bandeira escolhida para o desafio foi a bandeira do Brasil, claro!! rs.
Para iniciarmos o desafio primeiro precisamos criar um projeto no VSCode/Android Studio. No meu caso vou utilizar o VSCode, mas você pode utilizar a IDE/Editor de sua preferência.
Abra o VSCode e criei um novo projeto.
Depois escolha a opção Empty Application.
Selecione onde o projeto vai ser salvo
Agora precisamos dar um nome a esse projeto, coloque o nome que vc achar melhor. No meu caso coloquei o nome de custom_painter_flag
Com o nosso projeto já criado, vamos dar inicio a construção da nossa bandeira. Vamos criar uma arquivo chamado de home_page.dart
. Esse arquivo vai ser a página principal do nosso aplicativo.
Desenhando o fundo da bandeira
Agora vamos criar um arquivo chamado square.dart
. Lembrado que nosso foco aqui é a apenas em entender o CustomPainter, então não se atente a arquitetura, estrutura de pastas, etc.. Estou fazendo da forma mais simples possível.
Dentro do arquivo square.dart
vamos criar uma classe e vamos chama-lá de Square
. Essa classe vai estender a classe CustomPainter.
class Square extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
// TODO: implement shouldRepaint
throw UnimplementedError();
}
}
Voltamos no arquivo home_page.dart
e criamos o que eu chamo de lousa/quadro, onde vamos fazer a nossa arte, rs. Definimos um tamanho de 200 de altura de 300 de largura para nosso quadro. E para organizar nossos elementos vamos utilizar o Widget Stack
para posicionar nossos desenhos uns sobre os outros.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: Square(),
child: const SizedBox(
height: 200,
width: 300,
),
),
],
),
),
);
}
No arquivo square.dart
vamos implementar os métodos shouldRepaint
e paint
. No método shouldRepaint
vamos retornar false
a principio. O método shouldRepaint é chamado quando uma nova instância da classe é fornecida, para verificar se a nova instância realmente representa informações diferentes. O método paint é chamado sempre que o objeto personalizado precisa ser repintado e é nesse método que vamos fazer o nosso desenho.
class Square extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
O método paint
fornece um parâmetro chamado Size size
que o mesmo tamanho que definimos lá no nosso Widget SizedBox
arquivo home_page.dart
que foi de 300 de altura e 200 de largura.
CustomPaint(
painter: Square(),
child: const SizedBox(
height: 200,
width: 300,
),
),
Como já sabemos, precisamos desenhar um retângulo verde. Vou criar uma instância da classe Paint
e definir a cor dela como verde e também um instância da classe Path
onde vamos utilizar ela para desenhar nossas linhas. Agora precisamos definir os pontos que vamos percorrer para fazer as nossas linhas, como se fosse em um papel.
Vamos voltar as aulas de matemática e relembrar o plano cartesiano. Para pintar o nosso retângulo, vamos percorrer os eixos X e Y no nosso quadro.
A imagem abaixo representa os pontos que temos que percorrer para desenhar o retângulo
Agora vamos de fato começar a pintura!
Vamos mover para o ponto inicial [0,0]. A função moveTo(0, 0)
recebe os parâmetros X e Y.
> Obs: A partir de agora quando ver a escrita [Número, Número] entenda como [eixoX, eixoY]
class Square extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.green;
final path = Path()..moveTo(0, 0);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Agora vamos desenhar uma linha até o segundo ponto [0,200]. A função lineTo(0, size.height)
também recebe os parâmetros X e Y, como eu disse anteriormente o parâmetro size
fornecido pelo função paint
são os mesmos definidos no Widget SizedBox
então size.height = 200.
class Square extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.green;
final path = Path()..moveTo(0, 0)
..lineTo(0, size.height);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Vamos desenhar a próxima linha [300,200], lineTo(size.width, size.height)
class Square extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.green;
final path = Path()..moveTo(0, 0)
..lineTo(0, size.height)
..lineTo(size.width, size.height);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Se você chegou até aqui, provavelmente você deve estar vendo essa forma geométrica.
Calmaa... ainda precisamos finalizar os outros 2 pontos. Então vamos lá!!!
Vamos desenhar a próxima linha [300,0], lineTo(size.width, 0)
class Square extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.green;
final path = Path()
..moveTo(0, 0)
..lineTo(0, size.height)
..lineTo(size.width, size.height)
..lineTo(size.width, 0);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
E por fim, finalizamos voltando ao ponto inicial [0,0], lineTo(0, 0)
class Square extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.green;
final path = Path()
..moveTo(0, 0)
..lineTo(0, size.height)
..lineTo(size.width, size.height)
..lineTo(size.width, 0)
..lineTo(0, 0);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Agora sim você deve estar vendo o retângulo completo.
Primeira parte finalizada com sucesso!!! Agora vamos para criação do Losango.
Desenhando losango da bandeira
Vamos seguir a mesma linha do retângulo, primeiro vamos criar um arquivo chamado rhombus.dart
e em seguida criar a classe Rhombus que estende CustomPainter e já colocar o retorno no método shouldRepaint
como false
.
class Rhombus extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
No arquivo home_page.dart
vamos definir a largura de 250 e altura de 150 do nosso quadro para desenhar o losango.
CustomPaint(
painter: Rhombus(),
child: const SizedBox(
height: 150,
width: 250,
),
),
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: Square(),
child: const SizedBox(
height: 200,
width: 300,
),
),
CustomPaint(
painter: Rhombus(),
child: const SizedBox(
height: 150,
width: 250,
),
),
],
),
),
);
}
Segue imagem representando plano cartesiano do nosso quadro.
Segue imagem dos pontos que vamos ter que percorrer no quadro para desenhar o losango.
Bora colocar a mão na massa e iniciar a construção do nosso losango!!!
Primeiramente vamos criar a variável paint
e path
como já é de costume e movendo para ponto inicial [125,0] onde vamos começar a desenhar as linhas. Vamos criar também duas variáveis para definir onde está localizado o meio do eixo X(middleHorizontalFrame) e o meio do eixo Y(middleVerticalFrame).
class Rhombus extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.yellow;
final middleHorizontalFrame = size.width / 2;
final middleVerticalFrame = size.height / 2;
final path = Path()..moveTo(middleHorizontalFrame, 0);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Agora vamos desenhar uma linha até o ponto [0,75] lineTo(0, middleVerticalFrame)
class Rhombus extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.yellow;
final middleHorizontalFrame = size.width / 2;
final middleVerticalFrame = size.height / 2;
final path = Path()
..moveTo(middleHorizontalFrame, 0)
..lineTo(0, middleVerticalFrame);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
O próximo passo é desenhar uma linha até o ponto [125,150] lineTo(middleHorizontalFrame, size.height)
class Rhombus extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.yellow;
final middleHorizontalFrame = size.width / 2;
final middleVerticalFrame = size.height / 2;
final path = Path()
..moveTo(middleHorizontalFrame, 0)
..lineTo(0, middleVerticalFrame)
..lineTo(middleHorizontalFrame, size.height);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Agora vamos desenhar uma linha até o ponto [250,75] lineTo(size.width, middleVerticalFrame)
class Rhombus extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.yellow;
final middleHorizontalFrame = size.width / 2;
final middleVerticalFrame = size.height / 2;
final path = Path()
..moveTo(middleHorizontalFrame, 0)
..lineTo(0, middleVerticalFrame)
..lineTo(middleHorizontalFrame, size.height)
..lineTo(size.width, middleVerticalFrame);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Finalmente finalizamos voltando para o ponto inicial [125,0] lineTo(middleHorizontalFrame, 0)
class Rhombus extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.yellow;
final middleHorizontalFrame = size.width / 2;
final middleVerticalFrame = size.height / 2;
final path = Path()
..moveTo(middleHorizontalFrame, 0)
..lineTo(0, middleVerticalFrame)
..lineTo(middleHorizontalFrame, size.height)
..lineTo(size.width, middleVerticalFrame)
..lineTo(middleHorizontalFrame, 0);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Se tudo estiver certo, você deve estar vendo essa imagem abaixo.
Desenhando globo da bandeira
Para desenhar o globo da bandeira vamos utilizar um recurso diferente, vamos utilizar o Widget Container
para fazer isso.
Vamos criar um Container
com a altura e largura de 100 pixels, e utilizar a propriedade decoration
para decorar esse Container
. Na propriedade shape
do Widget BoxDecoration
vamos definir ela como o tipo BoxShape.circle
e a cor azul Colors.blue
. A propriedade clipBehavior
é pra "cortar" o Widget filho do Container
para não ultrapassar o círculo.
Container(
height: 100,
width: 100,
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
),
Agora vamos adicionar esse Container no método build
do arquivo home_page.dart
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: Square(),
child: const SizedBox(
height: 200,
width: 300,
),
),
CustomPaint(
painter: Rhombus(),
child: const SizedBox(
height: 150,
width: 250,
),
),
Container(
height: 100,
width: 100,
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
),
],
),
),
);
}
Agora nossa bandeira já está tomando forma.
Criando a faixa da bandeira
Agora precisamos criar a nossa faixa da bandeira, essa parte requer um pouco mais de atenção, pois ela é um pouco mais complexa que as desenvolvidas até aqui.
Para desenhar nossa faixa vamos utilizar o que chamamos de
Curva de Bézier.
Vamos criar um arquivo e chamar de flag_banner.dart
e criar a classe FlagBanner
com o método shouldRepaint
retornando false
.
class FlagBanner extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Dentro do arquivo home_page.dart
iremos criar o nosso quadro onde vamos pintar nossa faixa com uma altura de 100 pixels e largura 100 pixels, para ficar em conformidade com o tamanho do nosso globo que também é de 100 pixel de altura e largura.
CustomPaint(
painter: FlagBanner(),
child: const SizedBox(
height: 100,
width: 100,
),
),
Vamos envolver esse novo quadro em uma nova pilha de widgets com o Widget Stack
.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: Square(),
child: const SizedBox(
height: 200,
width: 300,
),
),
CustomPaint(
painter: Rhombus(),
child: const SizedBox(
height: 150,
width: 250,
),
),
Container(
height: 100,
width: 100,
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: FlagBanner(),
child: const SizedBox(
height: 100,
width: 100,
),
),
],
),
),
],
),
),
);
}
Voltando ao arquivo flag_banner.dart
, dentro do método paint
vamos fazer diferente agora, vamos criar uma instância da da classe Paint
e definir as propriedades color
para Colors.white
o style
para PaintingStyle.stroke
para pintar apenas as bordas do conteúdo e não o interior, o strokeWidth
igual a 20 para definir largura do traçado.
class FlagBanner extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 20;
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Para auxiliar na construção vamos utilizar o Figma para facilitar o entendimento. Vamos criar um Frame de dividir ele am 4 partes iguais na horizontal de na vertical. Pra isso iremos utilizar o LayoutGrid e alterar a propriedade size
para 24.
Vá até o menu localizado no canto superior esquerdo do Figma e seleciono a opção pen para iniciar o desenho da faixa.
Faça uma linha na diagonal da base o primeiro quadrado até o final do da base do último quadrado.
Logo em seguida vá até o menu novamente e selecione a opção Bend Tools.
Selecione a ponta e arraste até o final do quadro
No menu lateral direito altere o stroke para 20.
Aqui temos um noção de como deve ficar a nossa faixa.
Agora vamos transformar isso em código!!!
No arquivo flag_banner.dart
vamos dividir o nosso quadro em 4 partes como fizemos no Figma, e posicionar no ponto inicial [0,25] onde iremos traçar a linha diagonal. Como definimos nosso quadro com altura de 100 pixels, cada quadrado deverá ter 25 pixels, ou seja o valor da variável verticalFrames
será 25.
class FlagBanner extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 20;
final verticalFrames = size.height / 4;
final path = Path()
..moveTo(0, verticalFrames);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Para fazer a linha curva vamos utilizar o método cubicTo( double x1, double y1, double x2, double y2, double x3, double y3,)
. Segue exemplos dos pontos x1, y1, x2, y2... abaixo.
Vamos definir o ponto X1 no ponto final do nosso eixo X size.width
, que seria o mesmo que 100.
class FlagBanner extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 20;
// 100 / 4 = 25
final verticalFrames = size.height / 4;
final path = Path()
..moveTo(0, verticalFrames)
..cubicTo(
size.width // x1
// y1,
// x2,
// y2,
// x3,
// y3,
);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Agora vamos posicionar o y1 a 25 pixels de distância
class FlagBanner extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 20;
// 100 / 4 = 25
final verticalFrames = size.height / 4;
final path = Path()
..moveTo(0, verticalFrames)
..cubicTo(
size.width, // x1
verticalFrames // y1,
// x2,
// y2,
// x3,
// y3,
);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
As demais posições vão fica no mesmo lugar, x2, y2, x3 e y3.
class FlagBanner extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white
..style = PaintingStyle.stroke
..strokeWidth = 20;
// 100 / 4 = 25
final verticalFrames = size.height / 4;
final path = Path()
..moveTo(0, verticalFrames)
..cubicTo(
size.width, // x1
verticalFrames, // y1,
size.width, // x2,
size.height, // y2,
size.width, // x3,
size.height, // y3,
);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Se tudo estiver certo teremos a seguinte imagem abaixo.
Estamos quase finalizando a nossa bandeira, agora precisamos criar as estrelas.
Criando as estrelas
Pra criar as nossas estrelas, primeiro como de costume vamos criar o nosso arquivo star.dart
e a classe Star que estende CustomPainter
e já implementar o método shouldRepaint
que irá retornar false
.
class Star extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
// TODO: implement paint
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
No arquivo home_page.dart
vamos definir o tamanho do nosso quadro que a princípio será de 20 pixels de altura e largura.
CustomPaint(
painter: Star(),
child: SizedBox(
height: 20,
width: 20,
),
),
Pra podermos visualizar a nossa estrela, vamos envolver nosso quadro em um Widget Positioned
de definir as propriedades. bottom
e left
pra 70 pixels.
Nosso método build
ficará assim.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: Square(),
child: const SizedBox(
height: 200,
width: 300,
),
),
CustomPaint(
painter: Rhombus(),
child: const SizedBox(
height: 150,
width: 250,
),
),
Container(
height: 100,
width: 100,
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: FlagBanner(),
child: const SizedBox(
height: 100,
width: 100,
),
),
Positioned(
bottom: 70,
left: 70,
child: CustomPaint(
painter: Star(),
child: const SizedBox(
height: 20,
width: 20,
),
),
),
],
),
),
],
),
),
);
}
Seguindo o mesmo modelo da faixa da bandeira vamos utilizar o Figma pra auxiliar na construção da nossa estrela. Vamos criar um Frame adicionar LayoutGrid a ele e definir a propriedade size
para 12, assim vamos ter um quadro divido em 8 partes iguais.
Agora vamos traçar uma linha diagonal utilizando 3 quadrados na vertical e 3 na horizontal.
Agora vamos fazer outra linha na horizontal também utilizando 3 quadrados na vertical e 3 na horizontal, e assim vamos seguindo os pontos que temos que percorrer em linhas para criarmos a nossa estrela.
Segue exemplo completo
Agora vamos transformar isso em código!!
No arquivo star.dart
vamos implementar método paint
da classe Star
.
Primeiro passo é definir a cor da nossa estrela e dividir o nosso quadro em 8 partes iguais, igual ilustramos no Figma. Como definimos que o nosso quadro terá 20 pixels de altura e largura, cada parte(quadrado) terá 8 quadrados de 2,5 pixels.
class Star extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.white;
// Divide o quadro em 8 partes iguais, na vertical e horizontal
// 20 / 8 = 2,5, ou seja cada quadradinho do nosso quadro tem 2,5px
final verticalFrames = size.height / 8;
final horizontalFrames = size.width / 8;
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Para facilitar, vamos criar algumas variáveis onde já vamos deixar definido a quantidade de quadrados que vamos percorrer na vertical e horizontal no nosso quadro traçando as linhas.
class Star extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.white;
// Divide o quadro em 8 partes iguais, na vertical e horizontal
// 20 / 8= 2,5, ou seja cada quadradinho do nosso quadro tem 2,5px
final verticalFrames = size.height / 8;
final horizontalFrames = size.width / 8;
final threeVerticalFrames = verticalFrames * 3;
final fiveVerticalFrames = verticalFrames * 5;
final sixVerticalFrames = verticalFrames * 6;
final oneHorizontalFrames = horizontalFrames * 1;
final twoHorizontalFrames = horizontalFrames * 2;
final threeHorizontalFrames = horizontalFrames * 3;
final middleHorizontalFrame = size.width / 2;
final fiveHorizontalFrames = horizontalFrames * 5;
final sixHorizontalFrames = horizontalFrames * 6;
final sevenHorizontalFrames = horizontalFrames * 7;
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
O que fizemos basicamente foi, pegar o valor que representa um quadradinho do nosso quadro que é 2,5 e multiplicar pela quantidade de quadrados que queremos percorrer. Exemplo de 3 quadrados: 2,5px * 3 = 7,5px
Agora vamos dar inicio ao nosso desenho, primeiro vamos posicionar no ponto inicial do nosso quadro [10,0]. Lembrando que nosso quadro tem 20 pixels largura, logo 10 pixels representa o meio do nosso quadro, moveTo(middleHorizontalFrame, 0)
.
class Star extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.white;
// Divide o quadro em 8 partes iguais, na vertical e horizontal
// 20 / 8= 2,5, ou seja cada quadradinho do nosso quadro tem 2,5px
final verticalFrames = size.height / 8;
final horizontalFrames = size.width / 8;
final threeVerticalFrames = verticalFrames * 3;
final fiveVerticalFrames = verticalFrames * 5;
final sixVerticalFrames = verticalFrames * 6;
final oneHorizontalFrames = horizontalFrames * 1;
final twoHorizontalFrames = horizontalFrames * 2;
final threeHorizontalFrames = horizontalFrames * 3;
final middleHorizontalFrame = size.width / 2;
final fiveHorizontalFrames = horizontalFrames * 5;
final sixHorizontalFrames = horizontalFrames * 6;
final sevenHorizontalFrames = horizontalFrames * 7;
final path = Path()..moveTo(middleHorizontalFrame, 0);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Nosso próximo ponto vamos percorrer 3 quadrado na horizontal e vertical [7,5 , 7,5], lineTo(threeHorizontalFrames, threeVerticalFrames)
.
No próximo não percorreremos nenhum(0) quadrado na horizontal e 3 na vertical [0, 7,5], lineTo(0, threeVerticalFrames)
class Star extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.white;
// Divide o quadro em 8 partes iguais, na vertical e horizontal
// 20 / 8= 2,5, ou seja cada quadradinho do nosso quadro tem 2,5px
final verticalFrames = size.height / 8;
final horizontalFrames = size.width / 8;
final threeVerticalFrames = verticalFrames * 3;
final fiveVerticalFrames = verticalFrames * 5;
final sixVerticalFrames = verticalFrames * 6;
final oneHorizontalFrames = horizontalFrames * 1;
final twoHorizontalFrames = horizontalFrames * 2;
final threeHorizontalFrames = horizontalFrames * 3;
final middleHorizontalFrame = size.width / 2;
final fiveHorizontalFrames = horizontalFrames * 5;
final sixHorizontalFrames = horizontalFrames * 6;
final sevenHorizontalFrames = horizontalFrames * 7;
final path = Path()
..moveTo(middleHorizontalFrame, 0)
..lineTo(threeHorizontalFrames, threeVerticalFrames)
..lineTo(0, threeVerticalFrames);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Seguindo a mesma lógica do ponto anterior o próximo vamos percorrer 2 quadrados na horizontal e 5 na vertical [5, 12,5], lineTo(twoHorizontalFrames, fiveVerticalFrames)
.
class Star extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.white;
// Divide o quadro em 8 partes iguais, na vertical e horizontal
// 20 / 8= 2,5, ou seja cada quadradinho do nosso quadro tem 2,5px
final verticalFrames = size.height / 8;
final horizontalFrames = size.width / 8;
final threeVerticalFrames = verticalFrames * 3;
final fiveVerticalFrames = verticalFrames * 5;
final sixVerticalFrames = verticalFrames * 6;
final oneHorizontalFrames = horizontalFrames * 1;
final twoHorizontalFrames = horizontalFrames * 2;
final threeHorizontalFrames = horizontalFrames * 3;
final middleHorizontalFrame = size.width / 2;
final fiveHorizontalFrames = horizontalFrames * 5;
final sixHorizontalFrames = horizontalFrames * 6;
final sevenHorizontalFrames = horizontalFrames * 7;
final path = Path()
..moveTo(middleHorizontalFrame, 0)
..lineTo(threeHorizontalFrames, threeVerticalFrames)
..lineTo(0, threeVerticalFrames)
..lineTo(twoHorizontalFrames, fiveVerticalFrames);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
E pra finalizar, vamos seguir a lógica dos pontos anteriores vamos percorrer os demais pontos.
class Star extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = Colors.white;
// Divide o quadro em 8 partes iguais, na vertical e horizontal
// 20 / 8= 2,5, ou seja cada quadradinho do nosso quadro tem 2,5px
final verticalFrames = size.height / 8;
final horizontalFrames = size.width / 8;
final threeVerticalFrames = verticalFrames * 3;
final fiveVerticalFrames = verticalFrames * 5;
final sixVerticalFrames = verticalFrames * 6;
final oneHorizontalFrames = horizontalFrames * 1;
final twoHorizontalFrames = horizontalFrames * 2;
final threeHorizontalFrames = horizontalFrames * 3;
final middleHorizontalFrame = size.width / 2;
final fiveHorizontalFrames = horizontalFrames * 5;
final sixHorizontalFrames = horizontalFrames * 6;
final sevenHorizontalFrames = horizontalFrames * 7;
final path = Path()
..moveTo(middleHorizontalFrame, 0)
..lineTo(threeHorizontalFrames, threeVerticalFrames)
..lineTo(0, threeVerticalFrames)
..lineTo(twoHorizontalFrames, fiveVerticalFrames)
..lineTo(oneHorizontalFrames, size.height)
..lineTo(middleHorizontalFrame, sixVerticalFrames)
..lineTo(sevenHorizontalFrames, size.height)
..lineTo(sixHorizontalFrames, fiveVerticalFrames)
..lineTo(size.width, threeVerticalFrames)
..lineTo(fiveHorizontalFrames, threeVerticalFrames)
..lineTo(middleHorizontalFrame, 0);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
Se tudo estiver OK, nós teremos a seguinte imagem abaixo.
E pra finalizar vamos criar uma método para gerar nossas estrelas de forma aleatórias.
List<({double leftPositionStar, double bottomPositionStar, double starSize})>
_generateStars() {
final list = <({
double leftPositionStar,
double bottomPositionStar,
double starSize
})>[];
var bottomPositionStar = 0.0;
var leftPositionStar = 0.0;
var starSize = 2.0;
const spacingBetweenStars = 10;
while (list.length != 26) {
if (list.isEmpty) {
bottomPositionStar = 70;
leftPositionStar = 70;
starSize = 8;
final itemList = (
leftPositionStar: leftPositionStar,
bottomPositionStar: bottomPositionStar,
starSize: starSize
);
list.add(itemList);
} else {
bottomPositionStar = double.parse(
(Random.secure().nextInt(55) + 10).toString(),
);
leftPositionStar = double.parse(
(Random.secure().nextInt(55) + 10).toString(),
);
starSize = double.parse(
(Random.secure().nextInt(8) + 2).toString(),
);
final generatedItem = (
leftPositionStar: leftPositionStar,
bottomPositionStar: bottomPositionStar,
starSize: starSize
);
for (final item in list) {
if ((item.leftPositionStar - generatedItem.leftPositionStar).abs() <
spacingBetweenStars &&
(item.bottomPositionStar - generatedItem.bottomPositionStar)
.abs() <
spacingBetweenStars) {
continue;
}
}
list.add(generatedItem);
}
}
return list;
}
}
O método build
do arquivo home_page.dart
ficará da seguinte forma.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: Square(),
child: const SizedBox(
height: 200,
width: 300,
),
),
CustomPaint(
painter: Rhombus(),
child: const SizedBox(
height: 150,
width: 250,
),
),
Container(
height: 100,
width: 100,
clipBehavior: Clip.antiAlias,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.blue,
),
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: FlagBanner(),
child: const SizedBox(
height: 100,
width: 100,
),
),
..._generateStars().map(
(star) {
return Positioned(
bottom: star.bottomPositionStar,
left: star.leftPositionStar,
child: CustomPaint(
painter: Star(),
child: SizedBox(
height: star.starSize,
width: star.starSize,
),
),
);
},
),
],
),
),
],
),
),
);
}
E o resultado final foi esse!!!!
Obrigado! Espero que tenham gostado 😃
Repositório do GitHub
https://github.com/luisgustavoo/custom_painter_test
Referência
https://medium.com/flutterando/desenhe-o-que-quiser-com-custom-paint-no-flutter-b8557d794823
Posted on January 10, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.