Entendendo o CustomPainter do Flutter
Tags: flutter, dart
Published: 2024-01-10 15:37:41


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.

![New Project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38d7fvt0ysfy50iz7jq0.png)

Depois escolha a opção Empty Application.

![Empty Application](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/97rmb5vp2fdwbvgmeacu.png)


Selecione onde o projeto vai ser salvo


![Folder Project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axahi47fn0n5086rv2va.png)


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`

![Project Name](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e0v05yffqebbxi9dor38.png)



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.

![Home Page File](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ndecllirxm328qyzhc9e.png)



## 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.

![Square](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1ay3p2fo3467xu70cqb7.png)

Dentro do arquivo `square.dart` vamos criar uma classe e vamos chama-lá de `Square`. Essa classe vai estender a classe CustomPainter.

```dart
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.

```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,
              ),
            ),
          ],
        ),
      ),
    );
  }
```

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.

```dart
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.

```dart
 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.

![Plano Cartesiano](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/md5o4r6tj8wqgqf4mrfd.png)

A imagem abaixo representa os pontos que temos que percorrer para desenhar o retângulo

![Rectangle Points](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d872newj4rwgh3dzodmy.png)

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]_

```dart
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;
}
```

![Point [0,0]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbigfg6kkuc2f2v2pp4l.png)

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.   

```dart
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;
}
```

![Point [0,200]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/by04sfvydwmfig2gyj90.png)

Vamos desenhar a próxima linha [300,200], `lineTo(size.width, size.height)`

```dart
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;
}
```

![Point [300,200]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wdhu1mjgzz4bon9mxeew.png)

Se você chegou até aqui, provavelmente você deve estar vendo essa forma geométrica.

![Print device Point [300,200]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8ff3jgjguzl2t8blna6o.png)

Calmaa... ainda precisamos finalizar os outros 2 pontos. Então vamos lá!!!

Vamos desenhar a próxima linha [300,0], `lineTo(size.width, 0)`

```dart
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;
}
```
![Point [300,0]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/13jwzsu66cqe3phdu7vz.png)

E por fim, finalizamos voltando ao ponto inicial [0,0], `lineTo(0, 0)`

```dart
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;
}
```

![Point [0,0]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kffz342t5kumujgv2zhe.png)

Agora sim você deve estar vendo o retângulo completo.

![Print device Square](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fbbgxirfdq86zuhj9hsa.png)

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` .

![Rhombus File](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gyrijfsllcj2xhv0mrgj.png)

```dart
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.

```dart
 CustomPaint(
    painter: Rhombus(),
    child: const SizedBox(
      height: 150,
      width: 250,
    ),
  ),
```

```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,
              ),
            ),
          ],
        ),
      ),
    );
  }
```

Segue imagem representando plano cartesiano do nosso quadro.

![Lousa Losango](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y47t7kncb4jpfa9npz43.png)

Segue imagem dos pontos que vamos ter que percorrer no quadro para desenhar o losango.

![Points Losango](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u29wn1uoy0awi2t0a7fr.png)

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).

```dart
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;
}
```

![Point [125,0]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jdaj96xuapnarlluw91r.png)

Agora vamos desenhar uma linha até o ponto [0,75] `lineTo(0, middleVerticalFrame)`

```dart
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;
}
```

![Point [0,75]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q9c437qdij0gsey7w61s.png)

O próximo passo é desenhar uma linha até o ponto [125,150] `lineTo(middleHorizontalFrame, size.height)`

```dart
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;
}
```

![Point [125,150]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bbr7uufb1hlcqof5wp9f.png)

Agora vamos desenhar uma linha até o ponto [250,75] `lineTo(size.width, middleVerticalFrame)`

```dart
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;
}
```

![Point [250,75]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5r58c62x80lqdr7f9w18.png)

Finalmente finalizamos voltando para o ponto inicial [125,0] `lineTo(middleHorizontalFrame, 0)`

```dart
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;
}
```

![Point [125,0]](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ccjcwobkcxgy9xyxmsha.png)

Se tudo estiver certo, você deve estar vendo essa imagem abaixo.

![Print device Rhombus](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/98xirardx0fe13qo62y4.png)

## 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.

```dart
  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`

```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.

![Print device globe](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c8o3gcjmzhck4sf0j40b.png)


## 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](https://pt.wikipedia.org/wiki/Curva_de_B%C3%A9zier). 
Vamos criar um arquivo e chamar de `flag_banner.dart` e criar a classe `FlagBanner` com o método `shouldRepaint` retornando `false`.

![Flag Banner File](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/itd870apyemgjj9ee66u.png)

```dart
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.

```dart
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`.

```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,
              ),
              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.

```dart
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.


![Frame Flag Banner](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h2sbx8nsafhfpo6mwb7c.png)

Vá até o menu localizado no canto superior esquerdo do Figma e seleciono a opção **_pen_** para iniciar o desenho da faixa.

![Pen Figma](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x7w09qohvdtafxqdr92x.png)

Faça uma linha na diagonal da base o primeiro quadrado até o final do da base do último quadrado.

![Line pen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/che2fjyabt2ilncw6hm2.png)

Logo em seguida vá até o menu novamente e selecione a opção **_Bend Tools_**. 

![Bend Tools](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kgidjzx1c1hrk9veppgv.png)

Selecione a ponta e arraste até o final do quadro

![Drag line](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/snbaclngkfxdcse0oxg6.png)

No menu lateral direito altere o **_stroke_** para 20.

![Stroke 20](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/inj2q3zhzqidmedq3c5q.png)

Aqui temos um noção de como deve ficar a nossa faixa.

![Flag Banner Figma](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3sphcq35r3f89fv8e0mh.png)

#### 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.

```dart
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.

![cubicTo example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pndl0ewce5uu0jnj0o34.png)


Vamos definir o ponto X1 no ponto final do nosso eixo X `size.width`, que seria o mesmo que 100.

![X1 Point](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bdsmj873403ygzre587a.png)


```dart
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

![y1 point](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nnhmrgvqy9g3dmewfjuk.png)


```dart
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.

![Point x2,  y2, x3 e y3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y4l4nanhvsvlyhxwoc91.png)

```dart
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.

![Print device flag banner](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bex05cdbes2e6fzidnnb.png)

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`.

![Star file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0cjraggmwp3vagpoi7ja.png)

```dart
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.

```dart
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.

```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,
              ),
              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.

![Frame Star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fol6n9nsextt0q806wps.png)

Agora vamos traçar uma linha diagonal utilizando 3 quadrados na vertical e 3 na horizontal.

![Point 1 star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6x9x7gu8hgcmyrp4pup2.png)

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. 

![Poin 2 star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i3uw2xzih52bw6bm2fpc.png)

Segue exemplo completo

![Figma Star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w8u8dogjadu3zyh37gk1.png)


#### 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.  

```dart
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.


```dart
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

![Three square](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xkmss68ctnck3x1s6wan.png)

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)`.

![1 point star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ihhjdmm13lahs4i4r44o.png)

```dart
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)`. 

![2 point star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2t04l21pkrvi8ckbrzop.png)

No próximo não percorreremos nenhum(0) quadrado na horizontal e 3 na vertical [0, 7,5], `lineTo(0, threeVerticalFrames)`

![3 point star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5hgcl1fespu0gaq12uua.png)

```dart
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)`.

![3 point star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5jtewqmjh7phxkaykz87.png)

```dart
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.

```dart
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.

![Print device star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q2760ytai8pfud4ege04.png)

E pra finalizar vamos criar uma método para gerar nossas estrelas de forma aleatórias.

```dart
  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.

```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,
              ),
              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 :smiley:

![Final result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ht9k6ykvjpwilkew5zd8.png)

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