Tutorial: Dashboard com gráficos
Neste tutorial você vai construir um dashboard executivo em paisagem com 3 tipos de gráfico, texto rico, tabela de dados e múltiplos data sources. O resultado exporta para PDF, HTML (com Chart.js interativo), CSV e Excel.
Exemplo completo:
examples/07-dashboard
O que você vai aprender
- Definir múltiplos data sources em um relatório
- Usar
chartcom tiposbar,lineepie - Combinar gráficos,
richTextetableno mesmo relatório - Configurar orientação
landscape - Diferenças de renderização entre PDF e HTML
1. Configurar a página em paisagem
Para dashboards, a orientação paisagem dá mais espaço horizontal:
{
"pageSettings": {
"format": "A4",
"orientation": "landscape",
"margins": { "left": 30, "right": 30, "top": 30, "bottom": 30 }
}
}2. Definir múltiplos data sources
Um dashboard normalmente combina dados de fontes diferentes. Declare quantos precisar:
{
"dataSources": [
{
"id": "monthly",
"type": "list",
"schema": {
"month": "string",
"revenue": "number",
"expenses": "number",
"profit": "number",
"orders": "integer"
}
},
{
"id": "categories",
"type": "list",
"schema": {
"category": "string",
"total": "number",
"percentage": "number"
}
}
]
}Os dados correspondentes no data.json:
{
"monthly": [
{ "month": "Jan", "revenue": 185000, "expenses": 142000, "profit": 43000, "orders": 234 },
{ "month": "Fev", "revenue": 198000, "expenses": 148000, "profit": 50000, "orders": 256 }
],
"categories": [
{ "category": "Eletrônicos", "total": 1250000, "percentage": 43.9 },
{ "category": "Vestuário", "total": 680000, "percentage": 23.9 }
]
}3. Criar gráfico de barras
O gráfico de barras é ideal para comparar valores entre categorias. Cada series gera um grupo de barras:
{
"type": "chart",
"id": "bar_revenue",
"x": 30,
"y": 10,
"width": 350,
"height": 180,
"chartType": "bar",
"dataSourceId": "monthly",
"categoryField": "month",
"series": [
{ "name": "Receita", "valueField": "revenue", "color": "2196f3" },
{ "name": "Despesas", "valueField": "expenses", "color": "ff5722" }
],
"options": {
"showLegend": true,
"legendPosition": "bottom",
"showGridLines": true
}
}4. Criar gráfico de linhas
Linhas funcionam bem para tendências ao longo do tempo:
{
"type": "chart",
"id": "line_profit",
"x": 410,
"y": 10,
"width": 350,
"height": 180,
"chartType": "line",
"dataSourceId": "monthly",
"categoryField": "month",
"series": [
{ "name": "Lucro", "valueField": "profit", "color": "4caf50" },
{ "name": "Pedidos (x100)", "valueField": "orders", "color": "ff9800" }
]
}5. Criar gráfico de pizza
Pizza é perfeita para proporções. Usa sempre a primeira série, e cada categoria vira uma fatia:
{
"type": "chart",
"id": "pie_categories",
"x": 30,
"y": 200,
"width": 280,
"height": 190,
"chartType": "pie",
"dataSourceId": "categories",
"categoryField": "category",
"series": [
{ "name": "Vendas", "valueField": "total", "color": "2196f3" }
],
"options": {
"showLegend": true,
"legendPosition": "right"
}
}6. Adicionar tabela de dados
O table renderiza dados em formato tabular com header e zebra-striping:
{
"type": "table",
"id": "monthly_table",
"x": 340,
"y": 200,
"width": 420,
"dataSourceId": "monthly",
"showHeader": true,
"alternateRowColors": true,
"headerFill": "1565c0",
"columns": [
{ "id": "col_month", "header": "Mês", "binding": "month", "width": 80 },
{ "id": "col_revenue", "header": "Receita", "binding": "revenue", "width": 90, "format": "currency:BRL", "align": "right" },
{ "id": "col_profit", "header": "Lucro", "binding": "profit", "width": 80, "format": "currency:BRL", "align": "right" },
{ "id": "col_orders", "header": "Pedidos", "binding": "orders", "width": 60, "format": "integer", "align": "center" }
]
}7. Usar RichText para destaques
O richText permite texto inline com estilos mistos — perfeito para KPIs:
{
"type": "richText",
"id": "kpi_highlight",
"x": 500,
"y": 18,
"width": 280,
"spans": [
{ "content": "Receita total: ", "fontSize": 11, "color": "90caf9" },
{ "content": "R$ 2.847.500", "fontSize": 14, "bold": true, "color": "ffffff" },
{ "content": " | Lucro: ", "fontSize": 11, "color": "90caf9" },
{ "content": "R$ 982.350", "fontSize": 14, "bold": true, "color": "69f0ae" }
]
}Cada span é um trecho de texto com estilo independente. Propriedades disponíveis:
| Propriedade | Tipo | Descrição |
|---|---|---|
content | string | Texto do trecho |
fontSize | double | Tamanho da fonte |
bold | bool | Negrito |
italic | bool | Itálico |
underline | bool | Sublinhado |
color | string | Cor hex (sem #) |
8. Onde colocar cada elemento
Os gráficos e tabela ficam na banda summary (aparece uma vez), enquanto o header contém título e KPIs:
┌─────────────────────────────────────────────────────────────┐
│ Header: Título + KPIs em richText │
├─────────────────────────────────────────────────────────────┤
│ Summary: │
│ ┌────────────┐ ┌────────────┐ │
│ │ Bar Chart │ │ Line Chart │ │
│ │ (receita) │ │ (lucro) │ │
│ └────────────┘ └────────────┘ │
│ ┌────────────┐ ┌──────────────────────┐ │
│ │ Pie Chart │ │ Tabela mensal │ │
│ │ (categ.) │ │ Mês | Receita | ... │ │
│ └────────────┘ └──────────────────────┘ │
│ Nota de análise (richText) │
├─────────────────────────────────────────────────────────────┤
│ Footer: Confidencial | Página │
└─────────────────────────────────────────────────────────────┘Detail vazio
O detail band deve existir com height: 0 e elements: [] para o motor iterar os registros e alimentar os gráficos. Os gráficos referenciam o data source diretamente via dataSourceId.
9. Resultado por formato
| Formato | Como renderiza |
|---|---|
Gráficos nativos via package:pdf, tabela renderizada como grade | |
| HTML | Gráficos interativos via Chart.js 4 (CDN), tabela como <table> |
| CSV | Dados tabulares do detail (sem visual gráfico) |
| Excel | Dados tabulares em planilha .xlsx |
10. Gerar em Dart
import 'dart:convert';
import 'dart:io';
import 'package:sulfite_core/sulfite_core.dart';
void main() async {
final engine = SulfiteEngineImpl();
final report = await engine.parseReport(
File('examples/07-dashboard/report.json').readAsStringSync(),
);
final data = jsonDecode(
File('examples/07-dashboard/data.json').readAsStringSync(),
);
final ctx = await engine.processData(report, data);
// PDF
File('dashboard.pdf').writeAsBytesSync(await engine.renderToPdf(ctx));
// HTML (gráficos interativos)
File('dashboard.html').writeAsStringSync(await engine.renderToHtml(ctx));
print('Dashboard exportado!');
}Dicas
- No HTML, os gráficos são interativos (hover, tooltips). No PDF são imagens estáticas
- Ajuste
widtheheightdos gráficos para caber na página — se o conteúdo ultrapassar a altura disponível, o motor lançaTooManyPagesException - Use cores do Material Design (hex sem
#):2196f3(blue),4caf50(green),ff5722(deep orange) - Múltiplos data sources não precisam ter o mesmo número de registros
- Para barras empilhadas, adicione
"stacked": truenasoptions— as séries são desenhadas camada sobre camada em vez de lado a lado - Se uma série tiver
color: "000000"(padrão), o motor atribui automaticamente uma cor da paleta interna (kChartPalette) - Use as propriedades de borda (
hasBorder,borderColor,borderWidth,borderDashed,borderDashGap) para destacar a área do gráfico