Skip to main content

Custom Export Formats

The export system is intentionally extensible. FdcExportFormat is a const value object rather than an enum, so applications and add-on packages can define new formats without modifying the Community package.

1. Define the format

const markdownTable = FdcExportFormat(
'markdownTable',
label: 'Markdown',
fileExtension: 'md',
mimeType: 'text/markdown',
);

Choose a stable id. Equality is based on format identity metadata rather than the optional icon.

2. Implement a writer

class MarkdownTableWriter implements FdcExportWriter {
const MarkdownTableWriter();

@override
FdcExportPayload write(FdcExportWriterContext context) {
final buffer = StringBuffer();

if (context.options.includeHeaders) {
buffer.writeln(
'| ${context.columns.map(context.headerFor).join(' | ')} |',
);
buffer.writeln(
'| ${context.columns.map((_) => '---').join(' | ')} |',
);
}

for (final row in context.rows) {
final values = context.columns.map(
(column) => context.displayTextFor(row, column),
);
buffer.writeln('| ${values.join(' | ')} |');
}

return FdcTextExportPayload(buffer.toString());
}
}

The exporter has already resolved row scope and column order before write is called. Writers should focus on serialization.

FdcExportWriterContext provides helpers for common format models:

  • valueFor(row, column) for source values,
  • displayTextFor(row, column) for presentation text,
  • keyFor(column) for object-oriented output keys,
  • elementNameFor(column) for XML-style names,
  • headerFor(column) for tabular headers.

3. Register the writer

FdcExportRegistry.register(
markdownTable,
const MarkdownTableWriter(),
);

Now direct export can resolve the writer automatically:

final result = await FdcExporter.exportDataSet(
orders,
format: markdownTable,
);

Registered formats are also discovered by the grid export button's effective format list.

Passing a writer directly

Registration is optional when a one-off export already has the writer instance:

final result = await FdcExporter.exportDataSet(
orders,
format: markdownTable,
writer: const MarkdownTableWriter(),
);

Use the registry for application-wide or package-level format integrations. Pass a writer directly for localized or dynamically configured export flows.

Writer-specific options

Define a strongly typed options object by extending FdcExportWriterOptions:

final class MarkdownExportOptions extends FdcExportWriterOptions {
const MarkdownExportOptions({this.alignNumbers = true});

final bool alignNumbers;
}

Pass it through the exporter:

await FdcExporter.exportDataSet(
orders,
format: markdownTable,
writerOptions: const MarkdownExportOptions(),
);

The writer reads and validates context.writerOptions.

Cross-format settings belong in FdcExportOptions; writer-specific settings belong in an FdcExportWriterOptions subtype.

Text and binary payloads

Return text:

return FdcTextExportPayload(content);

Return binary bytes:

return FdcBinaryExportPayload(bytes);

Consumers can always read FdcExportResult.bytes, regardless of payload type.

Custom visual styles

Presentation-oriented formats can define a style type extending FdcExportFormatStyle and place defaults in FdcExportStyle.formats:

FdcExportStyle(
formats: {
markdownTable: markdownStyle,
},
)

The writer can resolve the style with:

final style = context.exportStyle.styleFor<MyExportStyle>(
context.format,
);

This is the same extension model used by application/subtree export defaults.