NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
pub.dev · #2650 most downloaded on pub.dev
Fast, complete CSV parser for Dart. Encode, decode, stream, query, and validate CSV data with automatic type inference and zero dependencies.
Last release 3 days ago
04 Oct 2026
Release timing varies
gaps range from 8 days to 3 months
Nearly every release is documented
notes for 17 of 17 stable releases
Nothing withdrawn
no release was ever pulled
5 months old
17 releases · first in 2026
One column per month.
The booleans an exporter actually writes now read as booleans.
The booleans an exporter actually writes now read as booleans.
CsvConfig.boolValues maps extra spellings onto true and false. Only
lowercase true/false were booleans, but Excel writes TRUE and FALSE,
databases export t/f, and survey tools use Yes/No, so all of those
arrived as strings:
const CsvConfig(boolValues: {
'TRUE': true, 'FALSE': false,
'Yes': true, 'No': false,
});Matching is exact, so each spelling you accept is listed. It applies only to
a value that read as text, the same rule nullValues follows, so a numeric
entry such as '1' has no effect because 1 has already read as a number.
nullValues is checked first, so a value in both reads as null.
nullValues crashed the decode. decodeWithHeaderstype 'Null' is not a subtype of type 'String'. The header row is neverThe batch decoder, decodeStrings and the streaming CsvDecoder agree on all
of this, checked at every chunk-split offset as usual.
Padding around values no longer turns numbers into text.
Padding around values no longer turns numbers into text.
CsvConfig.trimFields trims whitespace from both ends of an unquoted42 is the number 42 rather than the42, and NULL matches a configured null value. Headers areA quoted field is never trimmed, because the quotes already say where the value
begins and ends. skipInitialSpace stays the narrower option, dropping spaces
before a field starts, including before an opening quote.
A field of nothing but spaces trims to empty, so it reads exactly as a
genuinely empty field does. The suite asserts those two are the same rather
than leaving it to chance, and checks every case through the batch decoder and
the streaming one with the input split at every offset.
Month names in any language, supplied by you.
Month names in any language, supplied by you.
CsvConfig.monthNames, a lowercase-keyed map of month spellings to monthcsv_plus still bundles no locale data, and this is why: the names are a map you
own, so a file's language is something you state rather than something the
library guesses from a list it happens to ship.
It applies only when dateOrder is not iso, exactly like the English names,
and range checking is unchanged: 31 avril 2024 stays text.
Dates that name their month, such as 3 April 2024 , are read too.
Dates that name their month, such as 3 April 2024, are read too.
dateOrder set to dayFirst or monthFirst, csv_plus now also reads3 April 2024, April 3, 2024,03-Apr-2024, 1st Jan 24, with full names, three-letter abbreviations andSept, in any case, and an optional HH:mm or HH:mm:ss after them.31 April 2024 and 29 Feb 2023The default CsvDateOrder.iso is unchanged and still reads ISO-8601 only, so
nobody who has not opted into non-ISO dates sees anything new.
Month-first values start with a letter, and until now every date form started
with a digit, so the batch decoder only looked for dates in its numeric branch.
The streaming decoder already looked everywhere. Left alone, the two would have
disagreed about April 3 2024; the suite's check of every path at every chunk
split caught it before release, and both now resolve dates in the same places.
Month names in other languages are still out of scope.
Numeric dates like 03/04/2024 can now be read, once you say which order they use. That closes the one limitation the README listed.
Numeric dates like 03/04/2024 can now be read, once you say which order they
use. That closes the one limitation the README listed.
CsvConfig.dateOrder, taking a CsvDateOrder. iso, the default,dayFirst03/04/2024 as 3 April, monthFirst as 4 March./, - and . all separate, day and month may be one or two digits, and aHH:mm or HH:mm:ss is kept. A two-digit year follows theThe order is something you tell csv_plus, never something it works out. A CSV
carries no locale, so a file of 03/04/2024 values is genuinely ambiguous and
sniffing it would be wrong for half of all files. The default stays iso, so
nothing changes for anyone who does not set it.
Range checking is unchanged and still applies to the new forms: under
monthFirst a value like 25/12/2024 has no 25th month, so it stays text
rather than rolling over into another year, and 29 February is accepted only in
a leap year.
All three decode paths, the batch decoder, decodeStrings and the streaming
CsvDecoder, resolve dates through one shared function, and the suite checks
every case through all of them with the stream split at every offset, so they
cannot drift apart.
Named months (3 April 2024) are still out of scope; a decoderTransform
handles those.
Write a sentinel for a null, so a file can round-trip its nulls.
Write a sentinel for a null, so a file can round-trip its nulls.
CsvConfig(nullPlaceholder: 'NULL') writes that text for a null instead ofCOPY reads\N, and plenty of exports use NULL. Paired with nullValues the samenecessary quoting whatever quoteMode isalways or strings itnullValues deliberately never matches, so the'' and null stay distinguishable on the way back.Turn the spellings an exporter uses for a missing value into real nulls.
Turn the spellings an exporter uses for a missing value into real nulls.
CsvConfig(nullValues: {'NULL', 'NA', 'N/A'}) decodes those fields as nullThe rule is narrow on purpose, because it is what keeps the decoders in step:
"NULL" in the data survives.0, false) never reaches thedecodeStrings is unaffected, since its return type cannot hold a null.This was deferred twice over the one-parsing-semantics invariant. The suite now
checks batch and streaming agree on every case, with the streaming input split
at every possible offset, so a value straddling a chunk boundary is exercised
rather than assumed.
Read the padding some exporters leave after a delimiter.
Read the padding some exporters leave after a delimiter.
CsvConfig(skipInitialSpace: true) drops spaces sitting between a delimitera, "b, c" is three fields by the letter of the spec; spreadsheets andAll three decode paths honour it identically, and the new suite checks that on
every case by splitting the streaming input at every possible offset, so a run
of spaces crossing a chunk boundary is exercised rather than assumed.
Read the files other tools actually hand you: bytes in, bytes out, and the encodings Excel on Windows writes. Additive and backward-compatible.
Read the files other tools actually hand you: bytes in, bytes out, and the
encodings Excel on Windows writes. Additive and backward-compatible.
CsvCodec.decodeBytes decodes CSV straight from a byte list, alongsidedecodeBytesWithHeaders, decodeBytesToTable and decodeBytesToMaps. ThisPlatformFile.bytes from a file picker,rootBundle.load() for a bundled asset, and response.bodyBytes from an HTTPsep= hint and delimiter auto-detection all apply onCsvCodec.encodeToBytes returns UTF-8 bytes, ready for File.writeAsBytes, aCsvConfig(addBom: true) the outputCsvCharset picks the encoding when a file is not UTF-8. CsvCharset.latin1CsvCharset.windows1252 read the accented names and currency symbols thatexample/bytes_example.dart runs the whole path end to end, including theCsvConfig(parseDates: true) turns a field in ISO-8601 form into a real DateTime during a typed decode, across decode, decodeToTable, decodeToMaps, the
CsvConfig(parseDates: true) turns a field in ISO-8601 form into a real
DateTime during a typed decode, across decode, decodeToTable, decodeToMaps,
the streaming CsvDecoder, and bindBytes. Off by default.
Every date and time field is range-checked before parsing, so an impossible
value stays text instead of becoming the wrong date: DateTime.parse rolls
2024-13-45 over to 14 February 2025, and this does not. Ambiguous locale
formats, unpunctuated runs such as 20240131, and quoted fields all stay text.
Closes the only entry in the README's Limitations list. Adds a docs page at
/csv-dates and corrects the type-inference FAQ, which named a flag
(inferTypes) that does not exist.
Opt-in ISO-8601 date and date-time inference. Additive and
backward-compatible.
CsvConfig(parseDates: true) turns a field in ISO-8601 form into a realDateTime during a typed decode, instead of leaving it as text. It appliesdecode, decodeToTable, decodeToMaps, theCsvDecoder, and bindBytes. Off by default, so nothing changesYYYY-MM-DD; a time part may follow after a T or aZ or numeric offset. A value03/04/2024 stay text, as do quoted fields and20240131.DateTime.parse2024-13-45 over to 14 February 2025; csv_plus does not.FastDecoder.tryParseIsoDateTime exposes the same strict parser, andFastDecoder.inferType takes an optional parseDates flag.A DateTime encodes back to a form that decodes to the same value, so a
decode/encode round trip is lossless in both local and UTC.
The IndexNow key file is named after the key, so building before the real key was set left a PLACEHOLDER.txt behind. The .firebase cache is local depl
The IndexNow key file is named after the key, so building before the real
key was set left a PLACEHOLDER.txt behind. The .firebase cache is local
deploy state and should never have been tracked.
documentation field.Add CsvSchema.coerce, CsvTable.coerce, and CsvCodec.decodeWithSchema to convert each column's values to the type declared on its CsvColumnDef (int, do
Add CsvSchema.coerce, CsvTable.coerce, and CsvCodec.decodeWithSchema to
convert each column's values to the type declared on its CsvColumnDef
(int, double, num, bool, String, DateTime). A column with no schema entry
or a null type is left unchanged; CsvTable.coerce returns a copy and never
mutates the source.
Coercion throws CsvParseException (carrying the 0-based row and column) on
a value that cannot convert, or a null in a column declared nullable:false;
a null in a nullable column stays null. Completes the schema story: it
could already validate types, and can now coerce them.
Per-column type coercion driven by CsvSchema. Additive and
backward-compatible.
CsvSchema.coerce(headers, rows), CsvTable.coerce(schema), andCsvCodec.decodeWithSchema(input, schema) convert each column's values to theCsvColumnDef (int, double, num, bool, String,DateTime). A column with no schema entry, or a null type, is leftCsvTable.coerce returns a copy and never mutates the source.CsvParseException (carrying the 0-based row and column)nullable: false; a null in a nullable column stays null. This completes theCsvSchema could already validate types, and can now coerceAdd three decode-only CsvConfig options plus a codec convenience:
Add three decode-only CsvConfig options plus a codec convenience:
All three options apply across every decode path (batch typed, strings,
flexible, the typed decoders, table, maps, and the streaming decoder) and
are held to identical output by the conformance suite, which splits input
at every chunk boundary. Closes the comment-skipping and row-windowing
roadmap items.
Comment-line skipping, row windowing, and a header-keyed map decode. All
additions are backward-compatible: existing calls behave exactly as before.
CsvConfig(comment: '#') drops comment lines while decoding. The marker is# inside a quoted field orskipRows.CsvConfig(skipRows: n) skips leading rows before the header row is read,CsvConfig(maxRows: n) caps the number of data rows returned (the header isCsvCodec.decodeToMaps decodes straight to a List<Map<String, dynamic>>decodeToTable(input).toMaps().All three config options apply across every decode path (decode,
decodeStrings, decodeFlexible, the typed decoders, decodeToTable,
decodeToMaps, and the streaming CsvDecoder), and are held to identical
output by the conformance suite that splits input at every chunk boundary.
Documentation and metadata only; no library or API changes.
Documentation and metadata only; no library or API changes.
ColumnDef is renamed CsvColumnDef (a deprecated typedef keeps old code compiling).
First stable release: one documented parsing semantics across every
decode path, streaming you can trust, data-loss guards on type
inference, and public benchmark receipts. The API is now frozen under
semantic versioning.
The batch decoder, string decoder, and streaming decoder previously
disagreed on edge cases. All paths now produce identical output,
enforced by a conformance suite that also splits input at every chunk
boundary (test/conformance_test.dart).
[''], or [null]skipEmptyLines (default), rows of a single empty field are dropped;,,) are now always kept (previouslydecodeStrings dropped them)."a"x readsax (Excel behavior). Previously the batch decoder produced an extrahasHeader, the header row is read as raw strings on every01 stays 01 (previously typed then1), and decoderTransform is not applied to it.FastDecoder.inferType is now the single shared inference used by all
typed paths, with guards against silent corruption:
007), leading plus (+1), and surrounding' 42' typed differently per path).1e999) stay text.decodeIntegers / decodeDoubles / decodeBooleans throwCsvParseException (with row and column) on invalid cells instead ofemptyAs: fill0 / 0.0 and any non-truefalse).decodeBooleans truth table is documented and case-insensitive:true/1 and false/0.decodeFlexible uses config.quoteCharacter when restoring an"), and reads empty fields as'' instead of null when typing is off.CsvDecoder.bind and CsvEncoder.bind honor downstreamCsvDecoder.bindBytes / CsvEncoder.bindBytes for UTF-8 byteutf8 wiring.CsvFile.writeStream closes the file even when the source streamCsvConfig(strict: true) throws CsvParseException with row and
column on structurally malformed input (text after a closing quote,
unterminated quote) instead of recovering. Lenient stays the default.
decodeWithHeaders parsed the entire input twice; it is now singleCsvTable.map handed live row lists to its transform, so writingCsvTable.parse headers are raw strings (01 stays 01)."10" < "9" lexicographically.distinct() keys are type-aware (1, 1.0, and "1" are distinct)encodeGeneric<String> quotes strings containing delimiters insteadQuoteMode.always writes null as "".ColumnDef is renamed CsvColumnDef (a deprecated typedef keeps oldDelimiterDetector left the default csv_plus.dart namespace;package:csv_plus/decoder.dart to use it directly.CsvTable documents its mutation rule: table-returning methods copy,sortedBy() returns aFastest on every measured workload (decode, typed decode, autodetect,
quote-heavy, encode, decodeWithHeaders) against csv 8.0.0,
fast_csv 0.2.11, and serial_csv 0.5.2, on JIT and AOT. The reproducible
harness and full tables live in benchmark/compare/.
Topics: csv, csv-parser, serialization, data-processing, file-handling
lib/csv_plus.dart with library modules referenceCsvConfig : immutable configuration with presets: CsvConfig() , .excel() , .tsv() , .pipe()
CsvConfig: immutable configuration with presets: CsvConfig(), .excel(), .tsv(), .pipe()CsvConfig.copyWith(): create modified copiesQuoteMode enum: necessary, always, stringsCsvException, CsvParseException, CsvValidationException: typed error hierarchyFastEncoder: high-performance batch encoder with encode(), encodeStrings(), encodeGeneric<T>(), encodeMap()CsvEncoder: streaming encoder as StreamTransformer with bind(), convert(), startChunkedConversion()CsvEncoder.encodeField(): static helper for single-field quoting_needsQuoting() for multi-char delimiter supportFastDecoder: byte-level batch decoder with codeUnits parsing, labeled-loop control flow, first-byte type inferencedecode(), decodeStrings(), decodeFlexible(), decodeIntegers(), decodeDoubles(), decodeBooleans()CsvDecoder: chunked state-machine streaming decoder with bind(), convert(), startChunkedConversion()DelimiterDetector: frequency/consistency scoring across candidates [, ; \t |], BOM strip, sep= hintCsvCodec: main API with all decode/encode methods, presets, auto-detectionCsvCodec.decodeToTable(), decodeMap(), encodeMap()CsvCodec.decoder / encoder: streaming transformer gettersCsvCodecAdapter: Codec<List<List<dynamic>>, String> for dart:convert pipelines and .fuse()csvPlus, csvExcel, csvTsv: global convenience instancesCsvTable(), .withHeaders(), .fromData(), .fromMaps(), .parse(), .empty()operator [], cell(), cellByName(), setCell(), setCellByName(), column(), columnAt(), getColumn(), getColumnAt()addRow(), addRowFromMap(), addRows(), insertRow(), removeRow(), removeWhere()addColumn(), insertColumn(), removeColumn(), removeColumnAt(), renameColumn(), reorderColumns()where(), firstWhere(), any(), every(), range(), take(), skip(), distinct()sortBy(), sortByIndex(), sortByMultiple(), sort()transformColumn(), map(), fold<T>()count(), sum(), avg(), min(), max(), groupBy()toList(), toMaps(), toCsv(), toString(), toFormattedString(), copy()validate(), conformsTo(), inferSchema()row[0] (int) and row['name'] (String)set(), headerMap, hasHeaders, headers, containsHeader(), toMap(), getHeaderName(), toString()name, index, values, inferredType, nonNullCount, nullCount, uniqueCountcolumns, allowExtraColumns, allowMissingColumnsCsvSchema.infer(): infer types and nullability from datavalidate(): check required columns, types, nullability, patterns, custom validatorsColumnDef with name, type, required, nullable, pattern, validatorread(), readSync(), stream(), write(), writeSync(), writeRows(), writeStream(), append()utf8.decoder for stream operationsio/csv_file.dart: core library stays platform-independentYour coding agent can read these notes before it upgrades. Set up the MCP server →