Skip to content

Filtros de impressão

Parâmetros interativos que o usuário preenche antes de imprimir ou exportar o relatório. Definem variáveis injetadas no ScriptEngine e como placeholders em URLs de fontes externas.

Os filtros suportam lookups dinâmicos (opções carregadas via HTTP ou banco de dados), paginação, validação declarativa e dependências em cascata entre parâmetros.

Definindo parâmetros no JSON

Adicione o campo parameters ao ReportDefinition:

json
{
  "reportId": "orders_report",
  "reportName": "Pedidos por Período",
  "parameters": [
    {
      "id": "startDate",
      "label": "Data Início",
      "type": "date",
      "defaultValue": "2024-01-01",
      "required": true
    },
    {
      "id": "endDate",
      "label": "Data Fim",
      "type": "date",
      "defaultValue": "2024-12-31",
      "required": true,
      "validation": {
        "rule": "dateAfter",
        "compareWith": "startDate",
        "errorMessage": "Data Fim deve ser posterior à Data Início"
      }
    },
    {
      "id": "status",
      "label": "Status",
      "type": "select",
      "options": ["todos", "aprovado", "pendente"],
      "defaultValue": "todos"
    }
  ],
  "bands": [...]
}

Tipos de parâmetro

typeDescriçãoEntrada gerada
textTexto livreCampo de texto
numberNúmero (inteiro ou decimal)Campo numérico com validação
dateData (ISO 8601 yyyy-MM-dd)Date picker
datetimeData e hora (yyyy-MM-ddTHH:mm)Date + time picker
currencyValor monetário formatadoCampo com máscara monetária
selectSeleção entre opções (estáticas ou dinâmicas)Dropdown ou lookup

Campos format e rounding

Para os tipos date, datetime, currency e number é possível personalizar o formato de entrada/exibição:

json
{
  "id": "invoiceDate",
  "label": "Data da Nota",
  "type": "date",
  "format": "MM/dd/yyyy"
}
json
{
  "id": "totalValue",
  "label": "Valor Total",
  "type": "currency",
  "format": ".,2",
  "rounding": "half_even"
}
typeformatSignificado
datedd/MM/yyyy (padrão)Padrão intl para exibição e entrada
datetimedd/MM/yyyy (padrão)Aplica à parte de data; hora sempre HH:mm
currency".,2" (padrão)<milhar><decimal><casas> — ex.: ".,2"1.234,56; ",.2"1,234.56
numbernull (ilimitado)Número de casas decimais como string (ex.: "3")

O campo rounding aceita os mesmos modos de RoundingConfig: half_even (padrão), half_up, half_down, up, down, truncate. Aplica-se apenas a currency e number quando format define precisão.

Lookups dinâmicos

Um parâmetro select pode ter suas opções carregadas dinamicamente via lookup. Existem dois backends:

  • http — busca opções em uma URL (padrão, sem necessidade de declarar source)
  • db — executa uma query SQL em uma conexão do ConnectionRegistry

Lookup HTTP

json
{
  "id": "categoryId",
  "label": "Categoria",
  "type": "select",
  "required": true,
  "lookup": {
    "source": "http",
    "url": "https://api.example.com/categories",
    "valueField": "id",
    "labelField": "name",
    "method": "GET",
    "cacheSeconds": 300,
    "pageSize": 20,
    "searchOnOpen": true
  }
}

O campo "source": "http" pode ser omitido — documentos sem source são tratados como HTTP (compatibilidade retroativa).

Lookup de banco de dados

json
{
  "id": "productId",
  "label": "Produto",
  "type": "select",
  "required": true,
  "lookup": {
    "source": "db",
    "connectionRef": "main_db",
    "query": "SELECT id, name FROM products WHERE active = true AND ({_search} = '' OR name ILIKE '%' || {_search} || '%') ORDER BY name LIMIT {_page_size} OFFSET {_offset}",
    "valueField": "id",
    "labelField": "name",
    "cacheSeconds": 120,
    "pageSize": 25,
    "searchOnOpen": true
  }
}

A query usa placeholders {paramId} (mesmo padrão das URLs REST). Os seguintes são injetados automaticamente:

PlaceholderValor
{_search}Texto digitado na caixa de busca
{_page}Página atual (1-based)
{_page_size}Valor de pageSize (ou 0 quando sem paginação)
{_offset}(page - 1) * pageSize

Para substituir o nome do schema dinamicamente, use {_schema} na query — ele é resolvido a partir do campo extra.schema da DatabaseConnectionEntry antes da parametrização.

Propriedades comuns dos lookups

CampoTipoDefaultDescrição
sourcestring"http"Backend do lookup: "http" ou "db"
valueFieldstringCampo do resultado usado como valor enviado ao relatório
labelFieldstringCampo do resultado exibido ao usuário
cacheSecondsint300Tempo de cache em memória (0 = sem cache)
refreshOnDependencyChangeboolfalseRecarrega quando um parâmetro de dependsOn muda
pageSizeint0Itens por página (0 = busca tudo de uma vez)
searchOnOpenbooltrueCarrega os primeiros itens ao abrir o diálogo

Propriedades exclusivas do lookup HTTP

CampoTipoDefaultDescrição
urlstringURL com placeholders {paramId}
methodstring"GET"Método HTTP: "GET" ou "POST"

Propriedades exclusivas do lookup DB

CampoTipoDefaultDescrição
connectionRefstringID de uma entrada DatabaseConnectionEntry no ConnectionRegistry
querystringQuery SQL com placeholders {paramId}

Formatos de resposta HTTP aceitos

O HttpLookupResolver aceita dois formatos:

  • Lista raiz: [{"id": "1", "name": "São Paulo"}, ...]
  • Chave data: {"data": [{"id": "1", "name": "São Paulo"}, ...]}

Para paginação, o resolver lê meta da resposta para calcular hasMore:

json
{
  "data": [...],
  "meta": { "total": 100, "page": 2, "perPage": 20 }
}

Quando meta está ausente, usa a heurística items.length >= pageSize.

Dependências em cascata

Use dependsOn para criar filtros encadeados. Quando o valor de um parâmetro-pai muda, os filhos são recarregados automaticamente:

json
{
  "parameters": [
    {
      "id": "countryId",
      "label": "País",
      "type": "select",
      "lookup": {
        "url": "https://api.example.com/countries",
        "valueField": "id",
        "labelField": "name"
      }
    },
    {
      "id": "stateId",
      "label": "Estado",
      "type": "select",
      "dependsOn": ["countryId"],
      "lookup": {
        "url": "https://api.example.com/countries/{countryId}/states",
        "valueField": "id",
        "labelField": "name",
        "refreshOnDependencyChange": true
      }
    },
    {
      "id": "cityId",
      "label": "Cidade",
      "type": "select",
      "dependsOn": ["stateId"],
      "lookup": {
        "url": "https://api.example.com/states/{stateId}/cities",
        "valueField": "id",
        "labelField": "name",
        "refreshOnDependencyChange": true
      }
    }
  ]
}

Segurança (SSRF)

O HttpLookupResolver só aceita as seguintes origens (rejeita qualquer outra com ArgumentError):

  • https:// — qualquer host externo via HTTPS
  • http://localhost — desenvolvimento local
  • http://127.0.0.1 — loopback alternativo (útil em testes e servidores locais)

Validação declarativa

Cada parâmetro pode ter uma regra de validação inline via validation:

json
{
  "id": "quantity",
  "label": "Quantidade",
  "type": "number",
  "required": true,
  "validation": {
    "rule": "min",
    "value": "1",
    "errorMessage": "Quantidade mínima é 1"
  }
}

Regras disponíveis

ruleParâmetrosDescrição
requiredCampo não pode ser vazio
minLengthvalue ou compareWithComprimento mínimo do texto
maxLengthvalue ou compareWithComprimento máximo do texto
minvalue ou compareWithValor numérico mínimo
maxvalue ou compareWithValor numérico máximo
dateBeforevalue ou compareWithData deve ser anterior ao limite
dateAftervalue ou compareWithData deve ser posterior ao limite
lessThanvalue ou compareWithValor estritamente menor
greaterThanvalue ou compareWithValor estritamente maior
  • value — valor fixo para comparação (ex: "100", "2024-01-01")
  • compareWith — ID de outro parâmetro para comparação dinâmica (ex: "startDate")
  • errorMessage — mensagem exibida quando a validação falha

Injetando parâmetros nas expressões

Os valores dos parâmetros são injetados diretamente no contexto das expressões do ScriptEngine. Use os IDs dos parâmetros nas expressões de filtro:

json
{
  "op": "filter",
  "source": "orders",
  "where": "date >= startDate AND date <= endDate"
}
json
{
  "op": "filter",
  "source": "orders",
  "where": "status == orderStatus"
}

Operadores de comparação suportados para strings e datas (comparação lexicográfica): >=, <=, >, <, ==, !=

Injetando parâmetros em URLs REST

Use {paramId} como placeholder na URL da fonte de dados:

json
{
  "id": "orders",
  "type": "list",
  "source": "external",
  "url": "https://api.example.com/orders?from={startDate}&to={endDate}"
}

Os valores são URI-encoded automaticamente antes da requisição.

Usando no Studio

Parâmetros são editados no painel de propriedades do relatório (Configurações → Parâmetros):

  1. Clique em + para adicionar um parâmetro
  2. Preencha ID, rótulo, tipo, valor padrão e se é obrigatório
  3. Para tipo select, adicione as opções estáticas separadas por vírgula ou configure um lookup
  4. Escolha o backend do lookup (http ou db) e preencha os campos correspondentes
  5. Defina dependsOn para criar filtros encadeados
  6. Configure pageSize para ativar paginação no diálogo de seleção
  7. Adicione uma regra de validação se necessário

Ao clicar em Imprimir ou Salvar PDF, o Studio abre automaticamente o SulfiteFilterScreen se houver parâmetros definidos. O diálogo exibe cada parâmetro com o widget apropriado, validação inline e indicador de carregamento para lookups.

Passando parâmetros programaticamente

Via SulfiteEngine.generate():

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

final params = <String, String>{
  'startDate': '2024-06-01',
  'endDate': '2024-12-31',
  'orderStatus': 'aprovado',
};

final pdfBytes = await engine.generate(
  report: report,
  data: payload,
  params: params,
);

Usando o SulfiteFilterScreen standalone

O SulfiteFilterScreen pode ser usado fora do Studio como um diálogo independente:

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

final params = await SulfiteFilterScreen.show(
  context,
  parameters: report.parameters,
  initialValues: {'status': 'aprovado'},
  lookupResolver: HttpLookupResolver(),
);

if (params != null) {
  // Usar params para gerar o relatório
}

Resolver customizado

Implemente a interface LookupResolver para controlar como as opções são buscadas:

dart
class MyResolver implements LookupResolver {
  @override
  Future<List<LookupOption>> fetchOptions(
    LookupConfig config,
    ReportParams currentParams,
  ) async {
    // Sua lógica de busca — banco local, GraphQL, etc.
    return [
      LookupOption(value: '1', label: 'Opção A'),
      LookupOption(value: '2', label: 'Opção B'),
    ];
  }
}

Para lookups de banco, use o DbLookupResolver do pacote sulfite_datasources:

dart
import 'package:sulfite_datasources/sulfite_datasources.dart';

final registry = ConnectionRegistry();
registry.register(DatabaseConnectionEntry(
  id: 'main_db',
  host: 'localhost',
  port: 5432,
  database: 'myapp',
  username: 'user',
  password: 'secret',
));

final dbResolver = DbLookupResolver(registry: registry);

final params = await SulfiteFilterScreen.show(
  context,
  parameters: report.parameters,
  lookupResolver: dbResolver,
);

Referência do modelo

dart
// ReportParameter
ReportParameter(
  id: 'startDate',          // identificador único (sem espaços)
  label: 'Data Início',     // rótulo exibido no diálogo
  type: 'date',             // 'text' | 'number' | 'date' | 'datetime' | 'currency' | 'select'
  defaultValue: '2024-01-01',
  format: 'dd/MM/yyyy',     // opcional — formato de entrada/exibição
  rounding: 'half_even',    // opcional — para 'currency' e 'number'
  options: [],              // para type == 'select' (opções estáticas)
  required: true,           // bloqueia confirmação se vazio
  lookup: LookupConfig.http(  // HTTP lookup
    url: 'https://api.example.com/items',
    valueField: 'id',
    labelField: 'name',
    method: 'GET',
    cacheSeconds: 300,
    pageSize: 20,
    searchOnOpen: true,
    refreshOnDependencyChange: true,
  ),
  // --- OU ---
  lookup: LookupConfig.db(    // DB lookup
    connectionRef: 'main_db',
    query: 'SELECT id, name FROM categories WHERE {_search} = \'\' OR name ILIKE \'%\' || {_search} || \'%\' LIMIT {_page_size} OFFSET {_offset}',
    valueField: 'id',
    labelField: 'name',
    cacheSeconds: 120,
    pageSize: 25,
  ),
  dependsOn: ['parentParam'], // IDs dos parâmetros-pai
  validation: ValidationRule(  // opcional — validação inline
    rule: ValidationRuleType.min,
    value: '1',
    errorMessage: 'Valor mínimo é 1',
  ),
)

ReportParams é um typedef para Map<String, String>.

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