Tutorial: Tabela Pivot com dados inline
Neste tutorial você criará o relatório Arrecadação de ICMS por Estado — 2025: registros planos {estado, mes, icms} são transformados automaticamente em uma matriz Estado × Mês com totais por linha e coluna, usando o pivot mode do TableElement.
O que você vai aprender:
- Estruturar dados flat para consumo pivot
- Configurar
TablePivotConfigcomrowFields,columnField,valueFielde verb - Usar
height: 0emSummaryBandpara altura dinâmica (PDF) - Ativar totais por linha (
showRowTotals) e por coluna (showColumnTotals) - Configurar o pivot no Studio via editor de tabela
O código completo deste exemplo está em example/lib/examples/icms_pivot_report.dart.
Pré-requisitos
- Sulfite instalado (Instalação →)
- Familiaridade com data sources e bands
Passo 1 — Entender o modelo de dados
O pivot mode espera registros planos: um registro por célula da matriz final.
// 8 estados × 12 meses = 96 registros
[
{'estado': 'SP', 'mes': 'Jan', 'icms': 180.0},
{'estado': 'SP', 'mes': 'Fev', 'icms': 162.0},
// ...
{'estado': 'GO', 'mes': 'Dez', 'icms': 47.0},
]O renderer agrupa, pivota e agrega esses registros em tempo de renderização — o JSON do relatório não muda conforme os dados crescem.
Declare o data source normalmente:
{
"dataSources": [
{
"id": "arrecadacao",
"type": "list",
"schema": {
"estado": "string",
"mes": "string",
"icms": "number"
}
}
]
}Passo 2 — Configurar o pivot
No elemento table, adicione o campo pivot com TablePivotConfig. Quando pivot está presente, as columns são ignoradas — os cabeçalhos são gerados dinamicamente a partir dos valores distintos de columnField.
{
"type": "table",
"id": "pivot_icms",
"x": 0,
"y": 10,
"width": 782,
"height": 0,
"dataSourceId": "arrecadacao",
"showHeader": true,
"headerFill": "2e7d32",
"headerTextColor": "ffffff",
"headerBold": true,
"headerFontSize": 8,
"headerHeight": 20,
"rowHeight": 18,
"columnFontSize": 8,
"rowTextColor": "1b5e20",
"evenRowFill": "e8f5e9",
"oddRowFill": "ffffff",
"alternateRowColors": true,
"borderColor": "c8e6c9",
"borderWidth": 0.5,
"showRowBorders": true,
"showColumnBorders": true,
"columns": [],
"pivot": {
"rowFields": ["estado"],
"columnField": "mes",
"valueField": "icms",
"verb": "SUM",
"showRowTotals": true,
"showColumnTotals": true,
"totalLabel": "Total"
}
}Campos de pivot
Obrigatórios
| Campo | Descrição |
|---|---|
rowFields | Lista de campos que formam os rótulos de linha. Suporta múltiplos para agrupamento hierárquico (ex: ["regiao", "estado"]) |
columnField | Campo cujos valores distintos viram cabeçalhos de coluna (ex: "mes") |
valueField | Campo numérico a agregar em cada célula (ex: "icms") |
Agregação
| Campo | Padrão | Descrição |
|---|---|---|
verb | "SUM" | Função de agregação: "SUM", "AVG", "COUNT", "MIN", "MAX" |
O total de linha e o total de coluna respeitam o
verbescolhido. ParaAVG, o total é a média real de todos os valores — não a média das médias por célula.
Totais
| Campo | Padrão | Descrição |
|---|---|---|
showRowTotals | true | Adiciona coluna de total ao final de cada linha |
showColumnTotals | true | Adiciona linha de totais no rodapé da tabela |
totalLabel | "Total" | Rótulo da coluna/linha de totais. String vazia usa "Total" em tempo de renderização |
totalsFill | herda headerFill | Cor de fundo da linha de totais (hex sem #) |
totalsTextColor | herda headerTextColor | Cor do texto da linha de totais |
totalsBold | true | Texto em negrito na linha de totais |
totalsFontSize | herda columnFontSize | Tamanho de fonte da linha de totais |
Ordenação de colunas
| Campo | Padrão | Descrição |
|---|---|---|
columnOrder | [] | Lista de valores fixados no início, nessa ordem (ex: ["Jan","Fev",...]). Valores ausentes seguem columnSort |
columnSort | "alpha" | Estratégia para colunas não fixadas: "alpha" A→Z, "alpha_desc" Z→A, "value_asc" menor total primeiro, "value_desc" maior total primeiro, "natural" ordem de aparição nos dados |
Estilo avançado
| Campo | Padrão | Descrição |
|---|---|---|
columnOverrides | {} | Mapa { "colValue": TableColumnOverride } com overrides de largura, formato, alinhamento, negrito, cor de cabeçalho e conditionalStyles por coluna |
cellConditionalStyles | [] | Regras globais de estilo condicional aplicadas a todas as células de dados. columnOverrides[col].conditionalStyles tem prioridade. Primeira regra satisfeita vence |
Passo 3 — Usar SummaryBand com altura dinâmica
O número de linhas pivot varia conforme os dados. Use height: 0 na SummaryBand para que o PDF calcule a altura automaticamente:
{
"type": "summary",
"id": "summary_pivot",
"height": 0,
"elements": [
{ "type": "table", "id": "pivot_icms", ... }
]
}
height: 0em um band com um únicoTableElementativa o modo de altura dinâmica no PDF renderer. Os outros formatos (HTML, CSV, Excel) ignoram essa configuração.
Passo 4 — Dimensionar a tabela
Com rowFields: ["estado"] (1 campo), 12 meses e 1 coluna de total, a tabela tem 14 colunas. O renderer distribui width igualmente entre elas:
largura por coluna = width / total_colunas = 782 / 14 ≈ 55.9 ptPara A4 landscape com margens de 30pt:
largura disponível = 842 − 30 − 30 = 782 ptAjuste width conforme a orientação e as margens do seu relatório. Com muitas colunas, prefira A4 landscape ou reduza columnFontSize e headerFontSize para 7–8pt.
Passo 5 — Estrutura completa do relatório
{
"reportId": "icms_pivot_001",
"reportName": "Arrecadação de ICMS por Estado — 2025",
"pageSettings": {
"format": "A4",
"orientation": "landscape",
"margins": { "left": 30, "right": 30, "top": 30, "bottom": 30 }
},
"dataSources": [
{
"id": "arrecadacao",
"type": "list",
"schema": { "estado": "string", "mes": "string", "icms": "number" }
}
],
"bands": [
{
"type": "header",
"id": "header_main",
"height": 72,
"elements": [
{
"type": "rect",
"id": "header_bg",
"x": 0, "y": 0, "width": 782, "height": 72,
"fill": "1b5e20", "hasBorder": false
},
{
"type": "text",
"id": "title",
"x": 20, "y": 10, "width": 600, "height": 26,
"content": "ARRECADAÇÃO DE ICMS POR ESTADO",
"fontSize": 20, "bold": true, "color": "ffffff"
},
{
"type": "text",
"id": "subtitle",
"x": 20, "y": 38, "width": 580, "height": 14,
"content": "Demonstrativo mensal — Exercício 2025 · Valores em R$ milhões",
"fontSize": 10, "color": "a5d6a7"
}
]
},
{
"type": "summary",
"id": "summary_pivot",
"height": 0,
"elements": [
{
"type": "table",
"id": "pivot_icms",
"x": 0, "y": 10, "width": 782, "height": 0,
"dataSourceId": "arrecadacao",
"showHeader": true,
"headerFill": "2e7d32",
"headerTextColor": "ffffff",
"headerBold": true,
"headerFontSize": 8,
"headerHeight": 20,
"rowHeight": 18,
"columnFontSize": 8,
"rowTextColor": "1b5e20",
"evenRowFill": "e8f5e9",
"oddRowFill": "ffffff",
"alternateRowColors": true,
"borderColor": "c8e6c9",
"borderWidth": 0.5,
"showRowBorders": true,
"showColumnBorders": true,
"columns": [],
"pivot": {
"rowFields": ["estado"],
"columnField": "mes",
"valueField": "icms",
"verb": "SUM",
"showRowTotals": true,
"showColumnTotals": true,
"totalLabel": "Total"
}
}
]
},
{
"type": "footer",
"id": "footer_main",
"height": 24,
"elements": [
{
"type": "text",
"id": "footer_page",
"x": 580, "y": 6, "width": 202, "height": 11,
"content": "Página {pageNumber} de {totalPages}",
"fontSize": 7, "italic": true, "color": "bdbdbd", "align": "right"
}
]
}
]
}Passo 6 — Executar no exemplo
O exemplo já está registrado na demo app. Para rodá-lo:
cd sulfite/example
fvm flutter runSelecione "📊 ICMS por Estado (Pivot)" na lista de demos. O relatório gera em PDF e HTML com a mesma matrix pivotada.
Variações comuns
Pivot hierárquico (dois rowFields)
Agrupamento por região e estado — cada combinação vira uma linha:
"pivot": {
"rowFields": ["regiao", "estado"],
"columnField": "mes",
"valueField": "icms",
"verb": "SUM",
"showRowTotals": true,
"showColumnTotals": true,
"totalLabel": "Total"
}Contagem em vez de soma
Para contar quantas transações ocorreram por estado/mês:
"pivot": {
"rowFields": ["estado"],
"columnField": "mes",
"valueField": "quantidade",
"verb": "COUNT",
"showRowTotals": true,
"showColumnTotals": false
}Sem totais, label personalizado
"pivot": {
"rowFields": ["categoria"],
"columnField": "trimestre",
"valueField": "receita",
"verb": "AVG",
"showRowTotals": false,
"showColumnTotals": false,
"totalLabel": "Média Geral"
}Ordenar colunas por valor (maior total primeiro)
Use columnSort: "value_desc" para listar os meses com maior arrecadação à esquerda:
"pivot": {
"rowFields": ["estado"],
"columnField": "mes",
"valueField": "icms",
"verb": "SUM",
"columnSort": "value_desc",
"showRowTotals": true,
"showColumnTotals": true
}columnSort | Comportamento |
|---|---|
"alpha" | Alfabético A→Z (padrão) |
"alpha_desc" | Alfabético Z→A |
"value_asc" | Crescente pelo total agregado da coluna |
"value_desc" | Decrescente pelo total agregado da coluna (maior primeiro) |
"natural" | Ordem de aparição nos dados |
Dica: Para meses em português na ordem correta, use columnOrder para fixar a sequência e columnSort: "natural" para os demais:
"pivot": {
"rowFields": ["estado"],
"columnField": "mes",
"valueField": "icms",
"verb": "SUM",
"columnOrder": ["Jan", "Fev", "Mar", "Abr", "Mai", "Jun",
"Jul", "Ago", "Set", "Out", "Nov", "Dez"],
"columnSort": "natural"
}Overrides de estilo por coluna
Use columnOverrides para configurar largura, formato, alinhamento e cor de cabeçalho de colunas específicas:
"pivot": {
"rowFields": ["estado"],
"columnField": "mes",
"valueField": "icms",
"verb": "SUM",
"showRowTotals": true,
"columnOverrides": {
"Total": {
"bold": true,
"headerColor": "1a237e",
"align": "right",
"format": "#,##0.00"
},
"Dez": {
"headerColor": "b71c1c"
}
}
}Estilos condicionais nas células
Use cellConditionalStyles para colorir células com base nos dados. As regras são avaliadas em ordem e a primeira que satisfaz a condição é aplicada:
"pivot": {
"rowFields": ["estado"],
"columnField": "mes",
"valueField": "icms",
"verb": "SUM",
"cellConditionalStyles": [
{ "when": "value > 200", "fill": "e8f5e9", "bold": true },
{ "when": "value < 50", "textColor": "e53935" }
]
}Para aplicar estilos a uma coluna específica (com prioridade sobre cellConditionalStyles), use columnOverrides[col].conditionalStyles:
"columnOverrides": {
"Total": {
"conditionalStyles": [
{ "when": "value > 1000", "fill": "1b5e20", "textColor": "ffffff", "bold": true }
]
}
}O contexto de avaliação de cada regra contém todos os campos da linha mais value (valor bruto antes da formatação).
Configurar via Studio
No editor de tabela (clique duplo na tabela ou ícone de lápis), role até o card Modo Pivot:
- Ative o toggle Ativar Pivot
- Preencha Campos de Linha — nomes dos campos separados por vírgula (ex:
estado) - Preencha Campo de Coluna (ex:
mes) - Preencha Campo de Valor (ex:
icms) - Escolha a Agregação no dropdown (SUM / COUNT / AVG / MIN / MAX)
- Ative/desative Totais por Linha e Totais por Coluna
- Ajuste o Rótulo do Total se necessário
- Salve — o preview atualiza imediatamente
Desativar o toggle preserva a configuração anterior. Reativar restaura os campos já preenchidos.
Limitações conhecidas
| Limitação | Impacto |
|---|---|
| CSV/Excel | Exporta os dados planos não pivotados — o pivot ocorre apenas no PDF e HTML |
| Múltiplos valueFields | Um único valueField por configuração pivot. Para exibir várias métricas, use tabelas separadas |