Como manter suas impressões idênticas em todos os devices com o printer_gateway

Impressão térmica é aquele tipo de dor que todo mundo que trabalha com PDV, delivery, fiscal e automação comercial já sentiu: você manda o mesmo cupom, mas ele sai diferente em cada dispositivo.

  • Em uma impressora a fonte fica mais “gordinha”
  • Em outra, o espaçamento muda
  • Em outra, o SDK “interpreta” comandos de um jeito diferente
  • E no final… o seu layout vira loteria 😅

Foi exatamente pra resolver isso que eu publiquei o printer_gateway: um package Flutter que transforma seu recibo em uma imagem única e padronizada, garantindo que o resultado impresso fique idêntico em qualquer device — independente de modelo, fabricante ou SDK.

O problema real: imprimir linha a linha não é confiável

A abordagem tradicional costuma ser:

  1. Montar strings linha a linha
  2. Mandar comandos (ESC/POS, SDK do fabricante, “printText”, etc.)
  3. Torcer pra ficar igual em todas as impressoras

Só que na prática, cada impressora tem suas particularidades:

  • Fontes diferentes
  • Margens e espaçamentos variando
  • Renderização inconsistente do mesmo comando
  • Comportamentos específicos por SDK

Ou seja: você até “imprime”, mas não garante consistência.

A solução: gerar uma imagem única do cupom e imprimir a imagem

O printer_gateway segue uma ideia simples e poderosa:

Em vez de imprimir comandos, você gera um layout, renderiza isso como imagem, e imprime a imagem.

Isso elimina a variabilidade. O cupom “vira” um bitmap, então a impressora só recebe pixels. Resultado:

✅ Mesmo layout em qualquer device
✅ Mesmo espaçamento, alinhamento e fonte renderizada
✅ Melhor previsibilidade em produção
✅ Adeus “essa impressora ficou diferente”

Live Demo (pra testar agora)

Você consegue ver o package funcionando em tempo real aqui:

https://printer.brasizza.com

A ideia é você colar um JSON e já visualizar como o recibo fica renderizado.
https://gist.github.com/brasizza/2f9063c95e39a38d57b88fb46d698d17

O que o package faz

O printer_gateway converte JSON → recibo formatado e permite exportar esse recibo em 4 formatos:

  • JSON → Widget (preview no app)
  • JSON → Image (Uint8List) (imagem única do cupom)
  • JSON → POS printer (List) (quebra automática em partes)
  • JSON → ESC/POS printer (List<img.Image>) (pronto pra raster)

Além disso, ele suporta:

  • Texto com estilo (bold, italic, tamanho, alinhamento)
  • Divisórias
  • Tabelas (header + itens)
  • Colunas (ex.: label/valor)
  • QR Code
  • Header/Footer com imagem
  • Conteúdo sensível ofuscado automaticamente

Instalação

No seu pubspec.yaml:

dependencies:
  printer_gateway: latest

Depois

flutter pub get


Começando rápido

import 'package:printer_gateway/printer_gateway.dart';

final printerGateway = PrinterGateway(
  jsonData: jsonString,
);

// Opcional: adicionar imagens no header e footer
printerGateway.addHeaderImage(headerImageBytes);
printerGateway.addFooterImage(footerImageBytes);

1) Preview no app com toWidget()

Perfeito pra validar layout antes de imprimir.

Widget receiptWidget = printerGateway.toWidget(
  maxWidth: 576,
  margin: 10,
);

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: SingleChildScrollView(
      child: receiptWidget,
    ),
  );
}


Dica: use isso como “modo designer” do seu cupom. Ajusta no JSON, vê no widget, só depois imprime.

2) Gerar a imagem única com toImage()

Esse é o coração do package: gerar uma imagem final idêntica em todo lugar.

Uint8List imageBytes = await printerGateway.toImage(
  context,
  maxWidth: 576,
  margin: 0,
  fixedRatio: 2.0,
);
  • fixedRatio maior = mais qualidade (ótimo pra impressão)
  • você pode salvar em arquivo, enviar via API, cachear, etc.

3) POS printers com toPosPrinter()

Algumas impressoras têm limite de altura, então o package já resolve isso:

✅ gera imagem
✅ quebra automaticamente em “fatias”
✅ retorna List<Uint8List> pronta pra imprimir

List<Uint8List> parts = await printerGateway.toPosPrinter(
  context,
  maxHeight: 2000,
  maxWidth: 384,
);

for (final part in parts) {
  await yourPrinterService.printImage(part);
}

4) ESC/POS com toEscPosPrinter()

Pra quem usa libs ESC/POS que trabalham com image package:

import 'package:image/image.dart' as img;

List<img.Image> rasterImages = await printerGateway.toEscPosPrinter(
  context,
  maxHeight: 2000,
  maxWidth: 384,
);

for (final rasterImage in rasterImages) {
  await escPosPrinter.printRaster(rasterImage);
}

A “linguagem” do cupom: JSON

O layout do cupom é um array, onde cada item representa uma “linha”.

Texto simples

[
{
  "line": {
    "customization": {
      "font_style": { "bold": true, "italic": false },
      "font_size": 18,
      "alignment": 1
    },
    "content": "MY TEXT HERE"
  }
}
]

alignment:

  • 0 esquerda
  • 1 centro
  • 2 direita
  • 3 justificado

Divisor (linha horizontal)

{ "line": { "divider": true } }


QR Code

{
  "line": {
    "qrcode": {
      "size": 150,
      "content": "https://example.com/receipt/12345",
      "level": "L"
    }
  }
}



Espaçamento (linhas em branco)

{ "line": { "jump": 2 } }


Duas colunas (ótimo pra “Total / Valor”)

{
  "line": {
    "column": [
      { "row": { "customization": { "font_size": 12, "alignment": 0, "font_style": { "bold": false, "italic": false } }, "content": "Total", "size": null } },
      { "row": { "customization": { "font_size": 12, "alignment": 2, "font_style": { "bold": false, "italic": false } }, "content": "47,80", "size": 10 } }
    ]
  }
}

Tabela com header + itens (perfeito pra listagem de produtos)

O JSON suporta tabela com header e rows (igual no README), ideal pra cupons com muitos itens.

Conteúdo sensível? Ofusca automaticamente

Isso aqui é ouro pra segurança e LGPD: você manda o dado real em sensive_content e o package mascara automaticamente.

{
  "line": {
    "customization": { "font_size": 12, "alignment": 0, "font_style": { "bold": false, "italic": false } },
    "content": "Card Number: ",
    "sensive_content": "1234567890123456"
  }
}

Saída:

Card Number:  123**********56

Boas práticas (do mundo real)

  • Largura:
    • 58mm: maxWidth: 384
    • 80mm: maxWidth: 576
  • Qualidade: fixedRatio: 2.0 ou maior
  • Cupons longos: prefira toPosPrinter() / toEscPosPrinter() (quebra automática)
  • Workflow ideal:
    1. montar JSON
    2. validar com toWidget()
    3. imprimir via imagem

Links úteis

  • Pub.dev: https://pub.dev/packages/printer_gateway
  • Demo: https://printer.brasizza.com/
  • GitHub: https://github.com/brasizza/printer_gateway
  • Issues: https://github.com/brasizza/printer_gateway/issues