Como fazer campo de assinatura ou desenho em Flutter.
Tags: flutter, android, dart, signature
Published: 2021-02-08 04:28:46

## Introdução

Eai, clan. Tudo certo com vocês?

Hoje quero explicar como que eu fiz pra criar um campo de assinatura manual utilizando Flutter.

Estamos acostumados a utilizar assinatura digital para assinar documentos, mas existem alguns casos que ainda utilizam este tipo de assinatura. Mas a intenção deste post é para mostrar como você pode estar criando um campo onde pode ser rabiscado. Haha'

Então vamos partir do principio que temos que fazer uma assinatura em um determinado campo.

## Separei em dois modos

Eu acho muito importante nós utilizarmos algo que já tenha pronto, para não ficar reinventando a roda. Então eu separei em duas partes de como fazer este campo. 

A primeira maneira é utilizar um plugin Signature que esta disponível no pub.dev. É bem rápido e simples de utilizar.

Segunda é montar esse campo utilizando CustomPainter do Flutter, é um pouco mais trabalhoso  criar só que você consegue adicionar coisas mais especificas. 


## Utilizando Plugin Signature

Primeiro vai instanciar um controlador com as suas configurações de desenho. Por exemplo:

```
final SignatureController _controller = SignatureController(
    penStrokeWidth: 4,
    penColor: Colors.black,
    exportBackgroundColor: Colors.transparent,
  );
```

Neste controlador coloquei uma espessura de 4, cor preta e um fundo transparente. 

Próximo passa é passar este controlador para o Signature onde vamos assinar ou desenhar.

```
Signature(
       controller: widget._controller,
       height: 600,
       backgroundColor: Colors.transparent,
      )
```

Depois de assinar você pode pegar os bytes em png do controlador e transformar isso em uma imagem.

```
Image.memory(_controller.toPngBytes())
```



## Utilizando o Widget CustomPainter

Eu separei cada parte em uma classe que eu criei, você pode conferir o código completo no repositório do Git Hub. Link no final do artigo.

### Paint e Offset

Paint é uma descrição do estilo a ser usado ao desenhar em um Canvas, e Canvas é uma interface para registrar operações gráficas.

Então vamos criar uma classe responsável por isso.

```
class TouchPoints {
  Paint paint;
  Offset points;

  TouchPoints({this.points, this.paint});
}
```


### Criar campo para capturar o toque

Criar as variáveis para adicionar os pontos.

```
  GlobalKey globalKey = GlobalKey();

  List<TouchPoints> points = [];
  StrokeCap strokeType = StrokeCap.round;
  double strokeWidth = 3.0;
  Color selectedColor = Colors.black;
  double opacity = 1.0;
```


Criar campo para capturar evento do toque.

```
GestureDetector(
        onPanUpdate: (details) {
          setState(() {
            RenderBox renderBox = context.findRenderObject();
            points.add(_criarTouchPoints(renderBox, details));
          });
        },
        onPanStart: (details) {
          setState(() {
            RenderBox renderBox = context.findRenderObject();
            points.add(_criarTouchPoints(renderBox, details));
          });
        },
        onPanEnd: (details) {
          setState(() {
            points.add(null);
          });
        },
        child: RepaintBoundary(
          key: globalKey,
          child: CustomPaint(
            size: Size.infinite,
            painter: MyPainter(pointsList: points),
          ),
        ),
      ),
```

Função responsável por adicionar os pontos.

```
TouchPoints _criarTouchPoints(renderBox, details) {
    return TouchPoints(
        points: renderBox.globalToLocal(details.globalPosition),
        paint: Paint()
          ..strokeCap = strokeType
          ..isAntiAlias = true
          ..color = selectedColor.withOpacity(opacity)
          ..strokeWidth = strokeWidth);
  }
```

### Adicionar os pontos no Canvas

Classe usada no CustomPainter para desenhar.

```
@override
  void paint(Canvas canvas, Size size) {
    for (int i = 0; i < pointsList.length - 1; i++) {
      if (pointsList[i] != null && pointsList[i + 1] != null) {
        // Desenhar linha quando dois pontos consecutivos estiverem disponíveis
        canvas.drawLine(pointsList[i].points, pointsList[i + 1].points, pointsList[i].paint);
      } else if (pointsList[i] != null && pointsList[i + 1] == null) {
        offsetPoints.clear();
        offsetPoints.add(pointsList[i].points);
        offsetPoints.add(Offset(pointsList[i].points.dx + 0.1, pointsList[i].points.dy + 0.1));

        //Desenhe pontos quando dois pontos não estiverem próximos um do outro
        canvas.drawPoints(ui.PointMode.points, offsetPoints, pointsList[i].paint);
      }
    }
  }
```

### Transformar em uma imagem

Precisamos pegar todos os pontos rastreados no nosso Canvas e transformalo em bytes.

```
 RenderRepaintBoundary boundary = globalKey.currentContext.findRenderObject();
    ui.Image image = await boundary.toImage();
    ByteData byteData = await image.toByteData(format: ui.ImageByteFormat.png);
    Uint8List pngBytes = byteData.buffer.asUint8List();
```

Depois passar esses bytes para este Widget para criar uma imagem.

```
Image.memory(pngBytes)
```

## Conclusão

Aprender a fundo como as coisas funcionar é muito importante, pode esclarecer vários pontos que passa despercebido. 

Espero ter ajudado, valeu!



[GitHub](https://github.com/JoabeRamone/Assinatura-Manuel)

Referências:
[Plugin Signature](https://github.com/vemarav/signature)
[Flutter Paint Class](https://api.flutter.dev/flutter/dart-ui/Paint-class.html)	
[Flutter Canvas Class](https://api.flutter.dev/flutter/dart-ui/Canvas-class.html)	
[Flutter Offset Class](https://api.flutter.dev/flutter/dart-ui/Offset-class.html)
[Video do canal Flutter sobre CustomPaint](https://www.youtube.com/watch?v=vvI_NUXK00s&t=1s&ab_channel=Flutter)