Skip to content

Tutorial: Etiquetas de envio

Neste tutorial você criará etiquetas de envio no estilo logístico — duas etiquetas por folha A4, cada uma com logo, código de barras e endereço do destinatário. Usa o exemplo Etiqueta_de_Envio_Jadlog do repositório.

O que você vai aprender:

  • Configurar impressão de etiquetas com labelConfig
  • Usar RectElement com children como container de seção
  • Vincular um BarcodeElement a um campo de dados
  • Combinar ImageElement, FieldElement e RichTextElement em layout compacto

Pré-requisitos

  1. Sulfite instalado (Instalação →)
  2. Familiaridade com Bands e Data Sources

Passo 1 — Estrutura geral

O relatório tem apenas uma DetailBand que serve de template para cada etiqueta. O labelConfig cuida de replicar o template nas posições corretas da folha.

json
{
  "reportId": "jadlog_label_001",
  "reportName": "Etiqueta de Envio",
  "pageSettings": {
    "format": "A4",
    "orientation": "portrait",
    "margins": { "left": 14.17, "right": 14.17, "top": 14.17, "bottom": 14.17 },
    "labelConfig": {
      "format": "sheet",
      "columns": 2,
      "rows": 1,
      "labelWidthMm": 100.0,
      "labelHeightMm": 70.0,
      "columnGapMm": 5.0,
      "rowGapMm": 2.0,
      "sheetMarginTopMm": 5.0,
      "sheetMarginBottomMm": 5.0,
      "sheetMarginLeftMm": 5.0,
      "sheetMarginRightMm": 5.0
    }
  },
  "dataSources": [
    { "id": "shipments", "type": "list", "schema": {} }
  ],
  "bands": [
    {
      "type": "detail",
      "id": "label_body",
      "dataSourceId": "shipments",
      "height": 198.43,
      "elements": []
    }
  ]
}

labelConfig — parâmetros essenciais:

PropriedadeDescrição
format"sheet" (múltiplas por página) ou "roll" (contínuo)
columns / rowsGrade de etiquetas na folha
labelWidthMm / labelHeightMmDimensões de cada etiqueta em mm
columnGapMm / rowGapMmEspaçamento entre etiquetas
sheetMargin*MmMargens da folha em mm
presetNameNome de preset pré-configurado (opcional)

INFO

labelConfig só é suportado pelo PDF renderer. HTML, CSV e Excel ignoram esse campo.

Passo 2 — Logo da transportadora

Adicione um ImageElement no canto superior esquerdo:

json
{
  "type": "image",
  "id": "logo_carrier",
  "x": 5, "y": 5, "width": 80, "height": 30,
  "imageUrl": "https://sua-empresa.com/logo.png",
  "fit": "contain",
  "opacity": 1.0
}

Passo 3 — Cabeçalhos de seção com Rect children

Use RectElement com children para criar seções com fundo escuro e texto branco:

json
{
  "type": "rect",
  "id": "header_destinatario",
  "x": 0, "y": 65, "width": 110, "height": 15,
  "fill": "000000",
  "hasBorder": true,
  "borderColor": "000000",
  "children": [
    {
      "type": "text",
      "id": "label_dest",
      "x": 2, "y": 2,
      "content": "DESTINATÁRIO",
      "fontSize": 9,
      "bold": true,
      "color": "ffffff"
    }
  ]
}

O children permite aninhar elementos dentro de um RectElement, criando containers visuais sem band extra.

Passo 4 — Endereço do destinatário

Vincule um FieldElement a um campo com dot-path para acessar objetos aninhados:

json
{
  "type": "field",
  "id": "dest_address",
  "x": 8, "y": 88, "width": 100,
  "binding": "receiver.fullAddress",
  "fontSize": 9
}

Passo 5 — Código de barras

Use BarcodeElement com o prefixo binding: no campo value para vincular o código a um campo do registro:

json
{
  "type": "barcode",
  "id": "bc_shipment",
  "x": 115, "y": 85, "width": 165, "height": 60,
  "value": "binding:shipmentId",
  "format": "CODE128",
  "drawText": false
}
  • value: "binding:fieldName" — vincula o código ao campo fieldName do registro
  • formatCODE128 é o formato padrão para logística
  • drawText: false — omite o texto legível abaixo do código de barras

Passo 6 — Informações do remetente com RichText

Use RichTextElement para combinar trechos com estilos diferentes:

json
{
  "type": "richText",
  "id": "sender_info",
  "x": 8, "y": 169,
  "spans": [
    { "content": "Remetente: ", "fontSize": 8, "bold": true, "color": "000000" },
    { "content": "Empresa Teste\nRua Exemplo, 1234\nCidade - UF", "fontSize": 8, "color": "000000" }
  ]
}

Passo 7 — Payload e geração

dart
import 'dart:io';
import 'package:sulfite_core/sulfite_core.dart';

void main() async {
  final engine = SulfiteEngineImpl();
  final report = await engine.parseReport(reportJson);

  final pdfBytes = await engine.renderToPdf(report, {
    'shipments': [
      {
        'shipmentId': 'JD123456789BR',
        'orderSummary': '12345 / NF-001',
        'receiver': {
          'fullAddress': 'João da Silva\nRua das Flores, 100\nSão Paulo - SP\nCEP: 01310-100',
        },
      },
      {
        'shipmentId': 'JD987654321BR',
        'orderSummary': '12346 / NF-002',
        'receiver': {
          'fullAddress': 'Maria Souza\nAv. Paulista, 200\nSão Paulo - SP\nCEP: 01310-200',
        },
      },
    ],
  });

  await File('etiquetas.pdf').writeAsBytes(pdfBytes);
}

O engine distribui automaticamente os registros nas posições de etiqueta definidas pelo labelConfig (2 colunas × 1 linha por folha neste exemplo). Para mais de 2 registros, páginas adicionais são geradas automaticamente.

Diagrama da folha

┌─────────────────────────────────────┐
│  [Etiqueta 1]        [Etiqueta 2]   │
│  ┌──────────┐        ┌──────────┐   │
│  │ logo     │        │ logo     │   │
│  │ DEST.    │        │ DEST.    │   │
│  │ ||||||||│        │ ||||||||│   │
│  │ barcode  │        │ barcode  │   │
│  │ remetente│        │ remetente│   │
│  └──────────┘        └──────────┘   │
└─────────────────────────────────────┘

Presets disponíveis

PresetDimensões
Rolo 100×50mm100 × 50 mm, roll
Rolo 50×30mm50 × 30 mm, roll
Rolo 80×40mm80 × 40 mm, roll
Couché 50×30mm50 × 30 mm, sheet
A4 33×21mm33 × 21 mm, sheet
A4 50×30mm50 × 30 mm, sheet
A4 70×33mm70 × 33 mm, sheet
A4 100×42mm100 × 42 mm, sheet
A4 105×74mm105 × 74 mm, sheet

Para usar um preset, defina "presetName": "A4 100×42mm" e omita os campos de medida — eles serão preenchidos automaticamente.

Próximos passos

Sulfite do 🇧🇷 para o mundo © 2026 Rafael S. Pinheiro