excel_plus
Fast, low-memory Excel (.xlsx/.xls) library for Dart & Flutter to read, create, edit and style spreadsheets, charts, formulas and CSV. A faster drop-in for the excel package.
2.15.0
9.6K downloads/mo
#3003 most downloaded on pub.dev
almasumdev/excel_plus
What this package is like to depend on
Last release today
24 Aug 2026
Release timing varies
gaps range from 8 days to 2 months
Nearly every release is documented
notes for 34 of 35 stable releases
Nothing withdrawn
no release was ever pulled
4 months old
35 releases · first in 2026
35 releases in the last 12 months
see the full history below
Release timeline
35 releases · Apr 2026 to Aug 2026Releases
latest 35-
2.15.024 Aug 2026Release notes
Open source →Sheet.rowsAsMaps reads a worksheet as a List<Map<String, dynamic>>, taking
the keys from a header row. Every map holds a key for each column in the
used width, an empty header cell falls back to its column letter, and a
repeated name gets a _2 suffix, so no column is silently dropped.Sheet.toJson serialises one sheet, either as header-keyed objects or, with
headerRow: null, as an array of arrays. Excel.toJson serialises the whole
workbook keyed by sheet name, or a single named sheet.Values reuse the CSV bridge's scalar mapping, so numbers and booleans keep
their Dart types, dates and times become ISO-8601 strings, and a formula
exports its cached result. Adds a docs page at /excel-to-json.Release notes
Open source →Export a worksheet as JSON, or as plain Dart maps.
New
Sheet.rowsAsMaps()reads a worksheet as aList<Map<String, dynamic>>, taking the keys from a header row (row 0 by default, or any row viaheaderRow). Every map holds a key for each column in the sheet's used width, so all rows share the same keys and an empty cell reads asnull. An empty header cell falls back to its column letter and a repeated name gets a_2suffix, so no column is dropped. All-empty rows are skipped unlessskipEmptyRows: false.Sheet.toJson()serialises a worksheet to a JSON string, either as an array of header-keyed objects or, withheaderRow: null, as an array of arrays.pretty: trueindents the output.Excel.toJson()serialises the whole workbook to an object keyed by sheet name in worksheet order, or one named sheet withsheet:.
Values map the same way the CSV bridge maps them: numbers and booleans keep their Dart types, dates and times become ISO-8601 strings, a formula exports its cached result (or its text with
formulasAsText: true), and a cell error exports its literal. -
2.14.321 Aug 2026Release notes
Open source →The graph keyed formulas by a string joining sheet name, column and row
with a raw NUL separator, which put a literal 0x00 byte in the source.
Git then treated the file as binary, so it had no readable diff or blame.
The spill tracker in excel.dart already moved to a (name, row, column)
record; this applies the same shape here, which is also cheaper than
building a string per formula per recalculation.The writer test deliberately feeds U+0000 through U+0002 into a cell to
prove they are stripped on write. It now spells them as escapes, which is
identical at runtime and keeps that file readable too.No tracked text file in the repo contains a NUL byte any more.
Release notes
Open source →Changed
- Added a documentation website at https://excel-plus.web.app, with
task-based guides for reading, creating and editing workbooks, cell
styling, number formats, formulas, CSV, legacy
.xls, and large files. Linked from the package page via the newdocumentationfield.
- Added a documentation website at https://excel-plus.web.app, with
task-based guides for reading, creating and editing workbooks, cell
styling, number formats, formulas, CSV, legacy
-
2.14.215 Aug 2026Release notes
Open source →Documentation and source hygiene.
Changed
- Expanded the API reference. Every category (Core, Cell Values, Styling, Number Formats, Layout, Worksheet, Tables, Pivot Tables, Charts, CSV, and Errors) now opens with a short guide and a worked example instead of a one-line summary, and the documentation homepage leads with a quick-start example.
- Refreshed the README function count and roadmap; engineering and database functions already shipped.
- Added the styled-sheet preview to the package screenshots on pub.dev.
Fixed
- Removed stray NUL bytes from a source file that made it read as binary to some tools. The affected spill-tracking code now uses a typed key, with no change in behaviour.
-
2.14.115 Aug 2026Release notes
Open source →pub.dev allows five topics and all five were spent. topic:workbook holds
three packages and is effectively unbrowsed; topic:csv holds nine and is a
real browse category that excel_plus belongs in, since it has had CSV
import and export since 29511cd. topic:xls stays: it is only a two-package
category, but it is the one that separates this package from alternatives
that read .xlsx only.Release notes
Open source →Fixed
- A
<numFmt>declared with an id below 164 is now honoured instead of being ignored. Ids under 164 are nominally reserved for built-in formats, but Excel declares them for accounting and locale-specific codes; the reader dropped those declarations, so the cell rendered as General and the format code was lost on save. An explicit declaration is now authoritative for its id, and a file that repeats an id is read (last declaration wins) rather than throwing.
- A
-
2.13.015 Aug 2026Release notes
Open source →Add the database family (DSUM, DPRODUCT, DCOUNT, DCOUNTA, DAVERAGE, DMAX,
DMIN, DGET, DSTDEV, DSTDEVP, DVAR, DVARP) and engineering functions
(number-base conversions, BITAND/BITOR/BITXOR/BITLSHIFT/BITRSHIFT with
web-safe 24-bit-half math, and CONVERT for common length/mass/time/
temperature units). Doc and README updated; also corrected stale spilling
notes in doc/functions.md.Release notes
Open source →Database and engineering formula functions.
Added
- Database functions:
DSUM,DPRODUCT,DCOUNT,DCOUNTA,DAVERAGE,DMAX,DMIN,DGET,DSTDEV,DSTDEVP,DVAR,DVARP. Each takes a database range (first row is the headers), a field (a header name or a 1-based column number), and a criteria range. - Engineering functions: number-base conversions (
DEC2BIN/DEC2OCT/DEC2HEX,BIN2DEC/OCT2DEC/HEX2DEC, and the cross conversions), bitwiseBITAND,BITOR,BITXOR,BITLSHIFT,BITRSHIFT, andCONVERTfor common length, mass, time, and temperature units.
- Database functions:
-
2.12.504 Aug 2026Release notes
Open source →A styled but empty cell written self-closing () was skipped by the
SAX reader (no end event) and lost its style; a merged region's covered cells
were removed on read, losing the borders Excel draws from them. Self-closing
styled cells are now processed, and covered cells keep their style (only the
value is cleared). Refs #3.Release notes
Open source →Fixed
- Borders (and other styles) on empty cells and merged regions no longer
disappear on read and save. A styled empty cell written in self-closing form
(
<c s="1"/>) was dropped along with its style, and a merged region's covered cells were removed entirely, losing the borders Excel draws from them. Both are now kept. Thanks to @pamtbaau for the report (#3).
- Borders (and other styles) on empty cells and merged regions no longer
disappear on read and save. A styled empty cell written in self-closing form
(
-
2.12.429 Jul 2026Release notes
Open source →A relationship Target may be package-absolute (Target="/xl/worksheets/
sheet1.xml"), which Excel and several generators emit. The reader assumed a
relative target and built "xl//xl/..." then crashed on the null archive file.
Targets are now normalized (absolute or relative resolve alike), and a
worksheet that still cannot be found degrades to an empty sheet instead of
throwing. Refs #2.Release notes
Open source →Reading robustness fixes, all from issue #2 (thanks to @albertexye for the detailed reports and a sample file).
Fixed
- Reading an
.xlsxno longer drops, misplaces, or misreads text cells because of the shared-strings table. Two cases each shifted every later shared-string index: a duplicate entry (the reader was deduplicating on read, which is only correct when writing) and an empty self-closing<si/>(skipped while parsing). - Reading an
.xlsxwhose workbook relationships use absolute part paths (Target="/xl/worksheets/sheet1.xml", as Excel and several generators write them) no longer crashes. Absolute and relative Targets now both resolve, and a worksheet that cannot be located degrades to an empty sheet instead of throwing.
- Reading an
-
2.12.329 Jul 2026Release notes
Open source →parseEvents emits no end event for a self-closing tag, and the shared-string
reader only added entries on the end event, so an empty was skipped and
every later index shifted (later text cells then read the wrong value or null).
Reading now records a self-closing as an empty string at its own index.
Refs #2. -
2.12.228 Jul 2026Release notes
Open source →The reader deduplicated shared strings the way the writer does, so a file
whose sharedStrings.xml held duplicate entries had every later index
shifted down. Text cells after a duplicate then read the wrong string, and
cells past the shortened list returned null (dropped). Reading now appends
each at its own index; writing still deduplicates. Fixes #2. -
2.12.125 Jul 2026Release notes
Open source →Name charts, formulas, pivot tables and CSV in the pubspec description
and the README capability list, so the package surfaces for those
searches too. No code change.Release notes
Open source →Changed
- Broadened the package description and README to name more of what the library does (charts, formulas, pivot tables, CSV), for discoverability. No code change.
-
2.12.025 Jul 2026Release notes
Open source →Radar charts.
Added
Chart.radarauthors a radar (spider) chart. ChooseRadarStyle.standard,RadarStyle.marker(the default), orRadarStyle.filled. Radar charts also read back from an opened file, like the other chart types.
-
2.11.225 Jul 2026 -
2.11.125 Jul 2026Release notes
Open source →Formula recalculation fixes.
Fixed
- A defined name that refers to itself no longer overflows the stack during
recalculate(full or incremental). It resolves to#CIRC, the same as a self-referential cell.
Changed
recalculate(changed: ...)now recomputes the whole workbook when none of the given references parse, so a typo can't leave stale results. An empty list still does nothing.
- A defined name that refers to itself no longer overflows the stack during
-
2.11.025 Jul 2026Release notes
Open source →Incremental recalculation.
Added
Excel.recalculate({Iterable<String>? changed})can now recompute incrementally: pass the A1 references that changed (optionally sheet-qualified, e.g.['A1', 'Sheet2!B3'], ranges allowed) and only the formulas that transitively depend on them are recomputed, instead of the whole workbook. A static dependency graph built from the formula ASTs (with bounding-box edges for ranges and cross-sheet references) drives it. The result matches a full recalculate; a formula that uses a dynamic reference (INDIRECT/OFFSET) or a volatile function (NOW/TODAY/RAND) always recomputes, and each affected formula still spills exactly as in a full pass. Callingrecalculate()with no argument recomputes everything, exactly as before (no behaviour change).
-
2.10.025 Jul 2026Release notes
Open source →Typed CSV import via a schema.
Added
Excel.fromCsvandExcel.importCsvaccept an optionalschema(aCsvSchema, now re-exported from excel_plus along withCsvColumnDef). The first row is treated as the header and each named column's values are coerced to the declared type (int,double,num,bool,String,DateTime) instead of being inferred, so a column such as an id can be forced to stay text (007does not become7). A value that cannot be converted, or a null in anullable: falsecolumn, throwsCsvParseException. Requirescsv_plus: ^1.2.0.
-
2.9.025 Jul 2026Release notes
Open source →Broader image-format support for
Sheet.insertImage.Added
insertImagenow accepts BMP, TIFF, WebP, ICO, and the EMF and WMF metafiles, in addition to the existing PNG, JPEG, and GIF. Each is detected from its magic bytes, written with the correct OpenXML content type, and (unless a size is passed) has its intrinsic pixel size read from the header, so anchored pictures are sized correctly without a manualwidth/height. WebP dimensions are read from the VP8X, VP8 (lossy), and VP8L (lossless) chunks; TIFF from its first IFD; BMP, ICO, EMF, and WMF from their headers.
-
2.8.025 Jul 2026Release notes
Open source →Complete, Excel-correct dynamic-array spilling in
recalculate().Added
FormulaCellValue.spillRangereports the range a dynamic-array or array formula spilled into (for example"A1:C3"), ornullfor an ordinary single-value formula. It is set byrecalculate()and round-trips through a saved file's<f t="array" ref="...">.
Changed
recalculate()now spills array results (fromSEQUENCE,FILTER,SORT,UNIQUE, or a range like=A1:A3) the way Excel does:- A spill that would land on an already-occupied cell (a value or another
formula) resolves to
#SPILL!and leaves the blocking cells untouched, instead of silently overwriting them or spilling around them. - Cells a formula spilled into on a previous
recalculate()are cleared before it recomputes, so an array that shrinks no longer leaves stale values behind. - Two arrays that would overlap no longer fight: the first spills and the
second resolves to
#SPILL!.
- A spill that would land on an already-occupied cell (a value or another
formula) resolves to
Fixed
- A saved dynamic-array formula's spill range (
<f t="array" ref="...">) is now read back, so reopening a workbook and re-runningrecalculate()clears and refills the range correctly.
-
2.7.216 Jul 2026Release notes
Open source →Dependency and documentation update; no excel_plus API changes.
- Require
csv_plus: ^1.1.0. Its new decode-only options flow throughExcel.fromCsvandimportCsvvia their existingconfig:parameter:commentskips comment lines (for example#-prefixed),skipRowsdrops a leading preamble before the data, andmaxRowscaps how many rows are read. - Documented these CSV-import options in the README.
- Require
-
2.7.111 Jul 2026Release notes
Open source →Documentation only; no API or behaviour changes.
- Reorganised the generated API reference into twelve ordered categories, each with its own landing page: Core, Cell Values, Styling, Number Formats, Layout, Formulas, Worksheet, Tables, Pivot Tables, Charts, CSV, and Errors.
- Surfaced the built-in formula reference (engine, operators, and the full function list) as the Formulas category page.
- Enriched the library-level dartdoc and tidied the category descriptions.
-
2.7.011 Jul 2026Release notes
Open source →Added
- CSV import and export. Read and write CSV (and TSV, pipe-delimited, or any
custom-delimiter) data through a new bridge built on
csv_plus, a first-party, zero-dependency
package.
Excel.fromCsv(csv, {sheetName, inferTypes, config})builds a workbook from CSV text;excel.importCsv(csv, {sheetName, ...})adds a sheet to an existing workbook and returns it.sheet.toCsv({config, formulasAsText})andexcel.toCsv({sheet, ...})serialise a worksheet back to CSV.- Type inference is guarded against silent data loss (a value such as
007stays text); numbers, booleans, dates, times, errors, and formulas each map to a sensible CSV field. Pass aCsvConfig(re-exported from excel_plus) to control the delimiter, quoting, line ending, or BOM. - Pure Dart and web-safe on both dart2js and wasm; no
dart:ioon the CSV path.
- CSV import and export. Read and write CSV (and TSV, pipe-delimited, or any
custom-delimiter) data through a new bridge built on
csv_plus, a first-party, zero-dependency
package.
-
2.6.010 Jul 2026Release notes
Open source →Added
- Legacy
.xls(Excel 97-2003) files can now be opened.Excel.decodeBytesanddecodeBytesAsyncdetect the binary BIFF8 format from the file's magic bytes, so.xlsand.xlsxopen through the same call. The workbook is decoded read-only into the regular model: cell values, dates and times in both the 1900 and 1904 epoch systems, shared strings (including split and UTF-16 strings), merged cells, sheet order and tab visibility, built-in and custom number formats, fonts, fills, borders, alignment, and column widths and row heights. Formulas are decoded from their binary token streams back to real formula text (FormulaCellValue), covering the full operator set, the built-in function table, absolute and relative references, shared and array formulas, cross-sheet references, defined names, and constant arrays; the last-calculated result is kept as the cached value, and any token the decoder does not model degrades to that cached result instead of failing. Saving always produces a modern.xlsx, so opening an old file and saving it is a complete migration. Password-protected and pre-BIFF8 (Excel 5.0/95) files are rejected with clear typed errors. Pure Dart, no new dependencies, and works on every platform including the web.
Fixed
- Custom number-format codes are classified case-insensitively and bracket
prefixes are ignored. A format written as
M/D/YYYYwas treated as numeric, so its date cells decoded as plain serial numbers, and thedin a[Red]color prefix made currency formats such as[Red]-#,##0.00classify as dates. Both are fixed for.xlsxand.xls; elapsed-time brackets like[h]:mm:ssstill classify as time.
- Legacy
-
2.5.009 Jul 2026Release notes
Open source →Added
- Async decode and encode on a background isolate.
Excel.decodeBytesAsync(bytes)andexcel.encodeAsync()run the parse and the serialize-plus-zip work viaIsolate.run, so a Flutter app can open and save large workbooks without blocking the UI thread. Results return without copying, the calling instance is never mutated, and errors keep their types. On the web, where isolates are unavailable, both fall back to the main thread so shared code behaves identically.encodeAsyncthrows a clearExcelEncodeExceptionfor workbooks that cannot cross an isolate, such as one opened over a liveInputFileStream; useencode()there.
Performance
- Cell writes are about 55x faster, plain-file decode about 18x faster,
saves about 5x faster, and peak memory is roughly a third lower on large
workbooks. Color lookups now go through a map built once instead of
rebuilding the full palette on every access; value-only writes share one
canonical default
CellStyleper number format instead of allocating a fresh instance per cell (the style is copied privately on first read, so editing one cell never affects another); equality and hashing dropped derived fields that re-parsed hex strings per comparison; and the writer resolves each style once instead of re-fetching it per cell. Writing 100k mixed cells went from 5.1 s to 0.09 s, encode from 0.9 s to 0.13 s, and decode from 5.7 s to 0.31 s. A 1M-cell build, encode, and decode cycle went from about 18 s to 3.8 s, with peak memory down from 1.16 GB to 0.72 GB. - Style-heavy files decode about 8x faster and now scale linearly. The
styles parser re-walked the whole styles tree for every cell format; the
font list is now materialized once and indexed directly, scoped to the
<fonts>container so an out-of-range font id can no longer read an unrelated element. A 2,500-style workbook that took 12 s to open now opens in well under a second. - Saving into a heavily styled file no longer linear-scans its records. Authored styles resolve against the file's existing fills, gradients, and borders through O(1) reverse indexes, with first-occurrence semantics preserved.
Fixed
- A decode and encode round-trip no longer doubles the style records.
Every parsed cell style was re-appended to
styles.xmlas a fresh, unreferenced record on the first save, roughly doubling a style-heavy workbook's styles part per open-and-save cycle. Styles equal to a parsed record are now skipped. - Editing one cell's style no longer restyles every cell that shares its
format. In a decoded file all cells referencing the same format record
shared one
CellStyleobject, so a change throughcell.cellStylesilently applied to all of them. The getter now returns the cell's own private copy.
Documentation
- README benchmarks re-measured against
excel4.0.6 with this release's performance work: encode 6.5x to 7.5x, decode 3.3x, and create 3x to 3.5x faster at 1M and 5M cells. The raw numbers inbenchmark/compare/match.
- Async decode and encode on a background isolate.
-
2.4.002 Jul 2026Release notes
Open source →Added
InputStreamandInputFileStreamare re-exported, soExcel.decodeBuffer(InputFileStream('big.xlsx'))streams a large.xlsxstraight from disk without holding the whole compressed file in memory and without adding a separatearchivedependency. It reads a file path, so it is for native platforms; usedecodeBytesfor asset, network, or web bytes.
Fixed
- Adding a sparkline to a workbook that already contains sparklines no longer corrupts the block. The writer missed the prefixed container already present in the opened file and appended a second one, which made Excel keep only the first group and drop the newly added sparkline. The existing container is now reused, so original and added sparklines round-trip together. Introduced in 2.3.0; freshly authored sparklines were unaffected.
-
2.3.001 Jul 2026Release notes
Open source →Added
- Gradient cell fills.
CellStylegains an optionalgradientFill:GradientFill.linear(degree:, stops:)for an angled sweep orGradientFill.path(...)for a gradient radiating from an inner box, each blending two or moreGradientStops. A gradient takes precedence over a solid background or pattern. Gradients in opened workbooks read back ontoCellStyle.gradientFilland round-trip. - Autofilter filter criteria.
setAutoFiltergains acriteria:list ofFilterColumns that actually hide non-matching rows:FilterColumn.values(a checkbox list, optionally including blanks),FilterColumn.custom(one or two comparisons combined with AND/OR, with wildcard text matching), andFilterColumn.top10. Applied criteria read back onsheet.autoFilterColumnsand round-trip; unmodeled filter kinds are preserved untouched. - Conditional-formatting read-back. Rules in an opened workbook are parsed
into
sheet.conditionalFormats, exposing type, operator, formulas, colors, range, and a best-effortstylefor cell-is and formula rules. Read rules are for inspection; they round-trip untouched and are never duplicated. - Icon-set conditional formatting.
ConditionalFormat.iconSet(...)authors 3, 4, and 5-icon rules (arrows, traffic lights, flags, ratings, and more) with optional reverse order, hidden values, and custom thresholds. Icon-set rules also read back. - Sparklines. In-cell mini charts via
sheet.addSparklineGroup(SparklineGroup(...))or the single-cellsheet.addSparkline(...): line, column, and win-loss types with high/low/first/last/negative markers and colors. Groups read back onsheet.sparklineGroups; existing sparklines round-trip untouched. - Streaming save.
excel.encodeToStream(onBytes)writes the.xlsxto a callback chunk by chunk as the zip is produced instead of buffering the whole file, cutting peak memory for large workbooks.onBytesmatchesIOSink.add, and the output is byte-for-byte identical toencode().
Fixed
encode()andsave()are idempotent. Saving the same workbook instance more than once no longer appends duplicate font, format, or rule records; mutated parts are restored to their originally parsed state before every build.- Fills after a gradient no longer shift. The styles reader walks the
<fills>children directly, so a gradient fill (or a stray pattern inside a differential style) can no longer misalign later fills against their ids.
- Gradient cell fills.
-
2.2.101 Jul 2026Release notes
Open source →Fixed
- Workbooks no longer open with a repair prompt in Excel. The bundled template's theme part carried invalid XML (introduced in 2.1.0 when the template was regenerated), so every generated file was flagged as corrupt. The theme is repaired; affects 2.1.0 and 2.2.0. Thanks @gonojuarez (#1).
-
2.2.023 Jun 2026Release notes
Open source →Added
- Custom chart colors.
ChartSeriesgainscolor(fills the bars or area, or colors the line) andpointColors(per-slice colors for pie and doughnut charts, aligned to the values). Anything omitted falls back to the built-in Office palette, so existing charts are unchanged.
- Custom chart colors.
-
2.1.022 Jun 2026Release notes
Open source →Added
- Split panes.
sheet.splitPanes(xSplit:, ySplit:, topLeftCell:)creates independently scrolling panes (positions in twips), complementingfreezePanes. Read back viasheet.splitXandsheet.splitY; splits round-trip and are mutually exclusive with frozen panes. - More formula functions:
MAXIFS,MINIFS,DATEDIF,REPLACE,MROUND,ISEVEN,ISODD. XLOOKUPenhancements: wildcard match mode and reverse search mode.- Chart read-back. Charts in an opened workbook are parsed into
sheet.charts(type, title, series, categories, grouping, legend, axis titles, anchor). Existing charts still round-trip untouched. Chart.plotVisibleOnly. Set it tofalseto plot data kept in hidden rows and columns.Chart.anchorTo. When set, the chart is written as a two-cell anchor spanninganchortoanchorTo, so it lines up with the grid and resizes with the columns and rows instead of using a fixed pixel size.- Pivot-table read-back. Pivots in an opened workbook are parsed into
sheet.pivotTables(name, anchor, source range, row, column, page, and nested fields, and data fields with their aggregation). Existing pivots still round-trip untouched; an unmodeled pivot shape is preserved on save but omitted from the list.
Fixed
- Left-aligned cell padding (
indent) is no longer dropped. Indented left-aligned cells now emit an explicit left alignment so the padding applies. - No more orphaned drawing part. The blank-workbook template shipped an empty drawing, so the first chart or image left a stranded part that stricter importers could mishandle. Fresh charts and images now land in a single clean drawing part, and blank workbooks are smaller.
- Authored charts bake in cached values. Series embed their resolved values and category labels, so consumers that do not re-evaluate (notably LibreOffice, and charts over hidden rows) no longer draw empty plots.
- Authored chart series have explicit colors. LibreOffice rendered color-less series as invisible; each series and slice now gets an explicit Office accent color, and charts gain sensible gap, axis, and blank-handling defaults.
- Explicit column widths are no longer written as best-fit. Width-honoring apps (notably Google Sheets) re-fit such columns to their contents, collapsing empty columns and skewing merged layouts. A width set through the API now writes as a fixed width; only auto-fit columns keep the flag.
- The worksheet dimension reflects the real used range instead of the template's single-cell claim, so consumers that trust it no longer drop custom widths outside it.
- Authored charts get an explicit white background, so the chart and plot areas no longer render transparent in LibreOffice.
- Split panes.
-
2.0.021 Jun 2026Release notes
Open source →Breaking
-
Typed exception hierarchy. Failures now throw a sealed
ExcelExceptioninstead of the generic error types used before:ExcelArchiveException: the bytes are not a readable.xlsxcontainer. Replaces the oldUnsupportedErrorandArgumentErrorfor unreadable files.ExcelFormatException: a valid archive with malformed or inconsistent XML. Replaces the oldArgumentError.ExcelEncodeException: the workbook could not be encoded on save.FormulaParseException: raised inside the formula parser; it implementsFormatException, so existing handlers keep working. Through the public API a bad formula still surfaces as an#ERROR!cell value.
Each carries a
message, an optionalpart, and an optionalcause. Corrupt input was previously signalled withErrorsubtypes, which Dart reserves for programming bugs; bad input is an expected runtime condition, so it now throws anExceptionyou are meant to catch. Genuine argument validation still throwsArgumentError. To migrate, replaceon ArgumentError,on UnsupportedError, oron Erroraround decode calls withon ExcelExceptionor a specific subtype.
Fixed
- Pivot
<pivotCaches>workbook ordering. It was written in an invalid position that made Excel offer to repair files containing certain optional elements; it is now ordered correctly. - Formula serialization round-trip. Re-serialized shared formulas now re-double embedded quotes and single-quote non-identifier sheet names, so they parse back correctly.
- Criteria wildcards.
COUNTIF,SUMIF, and their multi-criteria variants honor*and?wildcards, with~as the literal escape. WEEKDAYsupports return types 11 to 17 and returns#NUM!for an unsupported type.INDEXwith a zero row or column returns the whole column or row as an array instead of#REF!.- Approximate
VLOOKUP,HLOOKUP,MATCH, andLOOKUPcompare within a value type, so a number is never matched against a text key. - Unary operators broadcast over arrays, matching the binary operators.
TEXTscaling commas. A comma after the last digit placeholder scales the value by 1000 per comma, distinct from a grouping comma.- Input validation and cleanup.
addPivotTablerejects field indices outside the source range instead of crashing on save,addChartrejects a chart with no series, andremoveTabledeletes the orphaned table part and its content-type entry.
Internal
- Replaced literal NUL bytes used as map-key delimiters with Unicode escapes
so the affected source files are plain text again; added
*.xlsxto.pubignore.
-
-
1.1.017 Jun 2026Release notes
Open source →Added
- Images (read and write). Embed pictures with
sheet.insertImage(bytes, anchor:, width:, height:)and read them back viasheet.images. PNG, JPEG, and GIF are supported; format and intrinsic size are detected from the bytes. Existing images are preserved and new ones are appended alongside them. - Page and print setup (read and write).
sheet.pageSetup = PageSetup(...)controls orientation, paper size, scaling, fit-to-page, centering, printed gridlines and headings, and margins with normal, wide, and narrow presets. - Print area, print titles, and manual page breaks.
setPrintArea,setPrintTitleRowsandsetPrintTitleColumnsfor repeated headers, andinsertRowPageBreakandinsertColumnPageBreak, each with matching getters and removers. All page-setup features are change-gated: an opened file keeps its existing setup byte-for-byte unless changed through the API. - Row and column grouping (read and write).
groupRows,groupColumns, and their ungroup counterparts nest outline levels; read levels and control visibility withsetRowHidden,setColumnHidden, and their getters. Outline state round-trips. - Cell comments (read and write).
sheet.setComment(index, Comment(...))orcell.commentattach classic notes; authoring writes the comments part and its legacy plumbing, and existing comments are read and preserved. - Workbook protection (read and write).
excel.protectWorkbook(...)locks the workbook structure and windows, with matching getters andunprotectWorkbook(). The optional password uses Excel's legacy hash. - Pattern fills (read and write).
CellStyle.fillPatterndraws a hatch or shade using the background color as the pattern color over an optional fill background. Non-solid patterns now survive a read round-trip. - Formula evaluation engine (opt-in).
sheet.evaluate(cell)computes a formula's value, andexcel.recalculate()recomputes every formula cell and stores the results so a saved file shows them. Around 130 built-in functions across math, statistics, criteria, logic, text, lookup, financial, and date and time, plus dynamic arrays (FILTER,SORT,UNIQUE,SEQUENCE). References resolve lazily with memoization and cycle detection; shared formulas are expanded on read; array results spill into their range on recalculate. Register custom functions withexcel.formula.registerFunction. Nothing runs during normal read or write. - Excel tables (read and write).
sheet.addTable(ExcelTable(...))turns a range into a named table with a styled header and autofilter; read viasheet.tablesand remove withremoveTable. Column names come from the header row or an explicit list, de-duplicated as Excel requires. Existing tables round-trip untouched. - Charts (authoring).
sheet.addChart(Chart.column(...))plus bar, line, area, pie, doughnut, and scatter constructors, each supporting multiple series, category labels, titles, legend position, grouping, and a pixel size anchored to a cell. Charts already in a file round-trip untouched. - Pivot tables (authoring).
sheet.addPivotTable(PivotTable(...))summarises a range with a row field and one or more measures; column fields, page fields, and nested row fields are supported. The cache is marked refresh-on-load so Excel rebuilds it on open. Existing pivots round-trip untouched.
Fixed
- Unmodeled parts survive a save. The archive cloner reused decoded zip entries directly, which the encoder re-wrote with a mismatched compression flag, corrupting untouched parts such as embedded media and printer settings. Parts are now carried across by value and re-compressed cleanly.
- Images (read and write). Embed pictures with
-
1.0.016 Jun 2026Release notes
Open source →First major release: a broad set of worksheet features built on the performance-focused engine, with a single contained breaking change. excel_plus remains a source-compatible drop-in for the
excelpackage.Breaking
CellValueis now sealed and gains aCellErrorValuemember. The only code affected is an exhaustiveswitchover aCellValue, which must now handleCellErrorValue. No other public type, method, or signature changed. Colour authoring is additive and existing literal colors behave exactly as before.
Added
- Theme color reading. Theme and tint color references resolve to real ARGB values from the workbook theme instead of falling back to black; the theme part round-trips.
- Indexed color reading. Legacy palette references resolve via the standard 64-color palette, honoring a workbook's palette override.
- Theme and indexed color authoring.
ExcelColor.theme(...)andExcelColor.indexed(n)write real references for font, fill, and border colors, so authored colors stay linked to the document theme. - Hyperlinks (read and write).
Hyperlink.url,Hyperlink.email, and internalHyperlink.locationjumps, each with optional display text and tooltip, set viasheet.setHyperlinkorcell.hyperlink. - Data validation (read and write). Dropdown lists, numeric and length bounds, and custom-formula rules, each with optional prompt and error message, applied to a cell or range.
- Sheet view settings (read and write). Freeze panes, gridline and header visibility, and zoom now round-trip instead of being dropped on save.
- Autofilter (read and write).
setAutoFilteradds header dropdowns over a range; files opened with applied criteria keep them. - Sheet protection (read and write).
sheet.protect(password:, allow:)with typed permission options; passwords use Excel's legacy hash and an opened file's existing hash is preserved. - Sheet tab color and visibility (read and write), including very-hidden sheets; untouched theme and indexed tab colors round-trip as references.
- Sheet reordering with
excel.moveSheetandexcel.sheetOrder. - Defined names (read and write), global or sheet-scoped, usable from formulas.
- Conditional formatting (authoring). Greater-than, less-than, equal,
between, and formula rules that apply a
CellStyle, plus two and three-color scales and data bars. Existing rules are preserved on save. CellErrorValue. Error cells such as#DIV/0!and#N/Aread as a typed value and write back, instead of being coerced to text.FormulaCellValue.cachedValue. A formula's last cached result is preserved on read and re-emitted on save, so formula cells keep a value until the app recalculates.CellStyle.indentfor alignment-side cell padding, with a full round-trip.
Fixed
- Rich-text preservation on write. Multi-run cells built with
TextCellValue.spanare written as styled runs instead of being flattened to plain text. - Authored styles that reuse an existing record no longer revert to the default style.
- Illegal XML control characters in cell text are stripped on save, so files no longer open as corrupt.
Excel.findAndReplacereturns the actual replacement count and accepts non-string targets.- On the web,
save()triggers the browser download under wasm builds as well as JS builds. - Underline styles distinguish single from double, and explicitly disabled bold and italic flags are honored.
- The parser no longer crashes on out-of-range shared-string or style indexes, ISO-8601 date cells, or namespace-prefixed worksheet XML.
- Cells without an explicit reference are positioned by column order, and multi-run inline strings keep all of their text.
getColumnWidthandgetRowHeightreturn Excel's defaults instead of throwing when a sheet defines none.- The header and footer element is written in its schema-correct position, so Excel no longer prompts to repair the file.
Improved
- Malformed number-format and border entries degrade gracefully instead of failing the whole parse.
-
0.0.512 Jun 2026Nothing published for this version
-
0.0.430 May 2026Release notes
Open source →- Upgraded the
xmldependency to^7.0.1and updated internal XML name handling for compatibility. - Reworked the example app into a real workbook demo with import, inline editing, styling, sheet tools, and export flows.
- Added a Validation Lab screen, a bundled workbook sample, and a safer temp-directory fallback when platform storage plugins are unavailable.
- Improved the example web bootstrap so debug runs use a compatible renderer
while wasm builds still opt into
skwasm.
- Upgraded the
-
0.0.314 Apr 2026Release notes
Open source →- Organized API docs into five categories: Core, Cell Values, Styling, Number Formats, Layout.
- Hid internal APIs from the public documentation.
- Improved dartdoc comments across all public classes and methods.
-
0.0.214 Apr 2026Release notes
Open source →- Removed the
collectionandequatabledependencies, reducing the package to three runtime dependencies:archive,xml,web. - Cleaned up dead code, duplicate utilities, and redundant comments.
- Consolidated XML escaping into a single shared utility and extracted a common date and time fraction helper.
- Fixed the minimum
xmlconstraint to^6.3.0for downgrade compatibility.
- Removed the
-
0.0.113 Apr 2026Release notes
Open source →- Initial release: a performance-optimized fork of the
excelpackage. - SAX-based streaming parser replaces full DOM parsing for cell data and shared strings.
- Lazy sheet loading: sheets are parsed on first access, not at file open.
- O(1) cell style lookup via a cached reverse index, smart archive cloning, and a fixed-point span correction algorithm with early termination.
- Fully API-compatible drop-in replacement for the
excelpackage.
- Initial release: a performance-optimized fork of the