dart_mappable
Improved json serialization and data classes with full support for generics, inheritance, customization and more.
4.8.0
450K downloads/mo
#547 most downloaded on pub.dev
schultek/dart_mappable
What this package is like to depend on
Last release 4 months ago
20 Apr 2026
Release timing varies
gaps range from 9 days to 7 months
Most releases are documented
notes for 79 of 97 stable releases
Nothing withdrawn
no release was ever pulled
5 years old
127 releases · first in 2021
3 releases in the last 12 months
see the full history below
Release timeline
127 releases · Apr 2021 to Apr 2026Releases
latest 60 of 127-
4.8.020 Apr 2026 -
4.7.008 Mar 2026Release notes
Open source →- Added
firstWheretoListCopyWithfor chaining copyWith calls on a list element based on a predicate function. - Add
useNodocoption for excluding generated classes from dartdoc output.
- Added
-
4.6.110 Sep 2025Release notes
Open source →- Record mappers now correctly uses hooks specified on the
@MappableRecordannotation. - Nested records are now correctly initialized, and record mappers are included in the generated
initializeMappers()method. - Getters are no longer falsely used in equality or stringify methods.
- Record mappers now correctly uses hooks specified on the
-
4.6.002 Aug 2025Release notes
Open source →- Add support for self-referencing generics (e.g.
T extends Comparable<T>) - Fix handling of nullable function fields.
- Disable formatting of generated files through
// dart format off. - Require
sdk: >=3.7.0.
- Add support for self-referencing generics (e.g.
-
4.5.013 Mar 2025Release notes
Open source →- Added
shallowEncodingoption to@MappableClass(). - Added
includeTypeIdoption to@MappableClass().
- Added
-
4.4.016 Feb 2025 -
4.3.107 Feb 2025 -
4.3.019 Oct 2024Release notes
Open source →- Added support for reusing annotations as constant variables.
- Added
SimpleMapper1BoundedandSimpleMapper2Boundedfor custom bounded generic types. - Fixed generation for generic functions.
- Fixed bug with nullable generic field.
- Fixed bug with import path on windows.
-
4.2.202 Apr 2024Release notes
Open source →- Added
CaseStyle.upperSnakeCase. - Fixed issues with adding unnecessary '__type' property for nullable generics.
- Improved serialization consistency and equality handling.
- Added
-
4.2.101 Mar 2024 -
4.2.025 Dec 2023Release notes
Open source →- Added custom typedef for mapping fields to resolve naming conflict.
- Deprecated creating and linking custom
MapperContainers. If you are affected by this see https://github.com/schultek/dart_mappable/issues/159.
-
4.1.006 Dec 2023Release notes
Open source →- Added support for shallow encoding a class:
MyClassMapper.ensureInitialized().encodeMap<MyClass>(myClass, EncodingOptions(shallow: true))
- Correctly escape
$in class names. - Added option to use unordered list equality with iterables:
IterableMapper.equalityMode = IterableEqualityMode.unordered
- Added
build_extensionsoption to builder configuration.
- Added support for shallow encoding a class:
-
4.0.121 Oct 2023 -
4.0.012 Oct 2023Release notes
Open source →Changelog
-
Require
sdk: >=3.0.0. -
Added support for Records.
-
Fields of a class can now be any record type.
-
You can annotate toplevel record typedefs:
@MappableRecord() typedef Coordinates = ({double latitude, double longitude});
-
Documentation
Record Fields
dart_mappablesupports record fields out of the box.
When you define a class, any field using a record type will be automatically included in the serialization.@MappableClass() class Location with LocationMappable { const Location({ required this.name, required this.coordinates, }); final (String, String) name; final ({double lat, double lng}) coordinates; }
An instance of this class would be encoded to:
{ "name": { "$1": "John", "$2": "Doe" }, "coordinates": { "lat": 123, "lng": 456 } }Record Aliases
Additionally, you can create record type aliases using annotated typedefs:
@MappableRecord() typedef Coordinates = ({double lat, double lng});
When using this type for a field of another class, it will be serialized as expected.
This also allows you to serialize records using the standalone generated mapper class as well as the generated extension methods:
void main() { // Using the generated mapper class. Coordinates coords = CoordinatesMapper.fromJson('{"lat": 123, "lng": 456}'); // Using the generated extension methods. String json = coords.toJson(); }
Renaming Record Properties
You can also annotate individual fields of a record alias to change the key or add a hook:
@MappableRecord() typedef FullName = (@MappableField(key: 'firstName') String, @MappableField(hook: MyHook()) String);
This will only work on annotated record type aliases, not inline record fields.
Note: Since typedefs are just an alias for a type, you cannot define two aliases for the same type
with different@MappableField()modifiers. Only one will be used for serialization.Release notes
Open source →-
Require
sdk: >=3.0.0. -
Added support for Records.
-
Fields of a class can now be any record type.
-
You can annotate toplevel record typedefs:
@MappableRecord() typedef Coordinates = ({double latitude, double longitude});
For a more detailed usage see the documentation.
-
-
-
4.0.0-dev.119 Sep 2023 pre-releaseNothing published for this version
-
4.0.0-dev.009 Jul 2023 pre-releaseNothing published for this version
-
3.3.111 Oct 2023Release notes
Open source →- Improved migration docs regarding
json_serializableand the difference oftoJson().
- Improved migration docs regarding
-
3.3.008 Oct 2023Release notes
Open source →- Make
DateTimeencoding configurable throughDateTimeMapper.encodingMode.
- Make
-
3.2.001 Sep 2023 -
3.1.308 Aug 2023 -
3.1.211 Jul 2023 -
3.1.109 Jun 2023Release notes
Open source →Changelog
-
The builder now respects basic initializer expressions of a constructor. This makes it
possible to do field renaming or assigning to private fields without requiring an additional
getter matching the parameter.The following is now supported out of the box:
class MyClass { MyClass(int value, {String? name}) // Effectively renaming 'value' to 'data'. : data = value, // Assigning to a private field + having a null fallback. _name = name ?? 'Unnamed'; final int value; final String _name;
-
Fixed encoding of a map now returns a
Map<String, dynamic>where possible
(instead of aMap<dynamic, dynamic>) -
Fixed supporting expressions in
@MappableClass.includeCustomMappers. -
Fixed error when using non-literal values in
@MappableValue(). -
Fixed generic decoding when using generic superclass.
-
Fixed unknown-type bug for serialized non-constructor fields.
-
Fixed bug with undetermined
includeSubClasses.
-
-
3.1.009 Jun 2023Release notes
Open source →-
Fixed error when using non-literal values in
@MappableValue(). -
The builder now respects basic initializer expressions of a constructor. This makes it possible to do field renaming or assigning to private fields without requiring an additional getter matching the parameter.
The following is now supported out of the box:
class MyClass { MyClass(int value, {String? name}) // Effectively renaming 'value' to 'data'. : data = value, // Assigning to a private field + having a null fallback. _name = name ?? 'Unnamed'; final int value; final String _name; -
Fixed encoding of a map now returns a
Map<String, dynamic>where possible (instead of aMap<dynamic, dynamic>)
-
-
3.0.119 May 2023Release notes
Open source →- Fixed dependency conflict with
package:collection. - Added topics for pub.dev.
- Fixed dependency conflict with
-
3.0.008 May 2023Release notes
Open source →Changelog
-
Breaking: Generated mappers no longer have a
.containerproperty. This was removed in favor
of the newMapperContainer.globalscontainer.// Instead of this: var value = MyClassMapper.container.fromValue(...); // Do this: var value = MapperContainer.globals.fromValue(...);
Mapper initialization and usage is now simplified to the following:
- When used explicitly (e.g. through
MyClassMapper.fromMapormyClass.toMap()) no additional
initialization is needed. - When used implicitly (through a generic type e.g.
MapperContainer.globals.fromMap<MyClass>()) the
mapper needs to be initialized once before being used withMyClassMapper.ensureInitialized().
- When used explicitly (e.g. through
-
Breaking: Changed internal mapper implementation which causes any custom mapper to break.
- Removed
MapperElementBaseclass. - Added
MappingContextbeing passed to mapper methods.
See docs on how to use custom mappers in v3.
- Removed
-
Breaking: Removed
@MappableLib.createCombinedContainerin favor of@MappableLib.generateInitializerForScope.Instead of generating a new container, v3 generates an initialization function for all mappers. Use it early on in your
application:@MappableLib(generateInitializerForScope: InitializerScope.package) library main; import 'main.init.dart'; void main() { initializeMappers(); ... }
-
Breaking: Improved support and features for
.copyWith.- Copy-With now supports classes that implement multiple interfaces.
- Renamed
.copyWith.apply()method to.copyWith.$update(). - Added
.copyWith.$merge()and.copyWith.$delta().
You can now use
.copyWithwith either an existing instance using.$mergeor a map of values using.$delta.@MappableClass() class A with AMappable { A(this.a, this.b); int? a; int? b; } void main() { var a = A(1, null); var c = a.copyWith.$merge(A(null, 2)); assert(c == A(1, 2)); var d = a.copyWith.$delta({'b': 2}); assert(d == A(1, 2)); }
-
Breaking: Removed
CheckTypesHookin favor of discriminator functions.You can now use a custom predicate function as the
discriminatorValueof a class. This function can check
whether the encoded value should be decoded to this subclass and return a boolean.@MappableClass() abstract class A with AMappable { A(); } @MappableClass(discriminatorValue: B.checkType) class B extends A with BMappable { B(); /// checks if [value] should be decoded to [B] static bool checkType(value) { return value is Map && value['isB'] == true; } } @MappableClass(discriminatorValue: C.checkType) class C extends A with CMappable { C(); /// checks if [value] should be decoded to [C] static bool checkType(value) { return value is Map && value['isWhat'] == 'C'; } }
-
Added support for serializing fields that are not part of the constructor
when annotated with@MappableField(). -
Added
EncodingOptionstotoValuemethod. -
Added support for third-party models by using annotated
typedefs. -
Added
renameMethodsto build options. -
Improved performance of generated encoding and decoding methods.
For a detailed migration guide, see this issue.
Release notes
Open source →-
Breaking: Generated mappers no longer have a
.containerproperty. This was removed in favor of the newMapperContainer.globalscontainer.// Instead of this: var value = MyClassMapper.container.fromValue(...); // Do this: var value = MapperContainer.globals.fromValue(...);Mapper initialization and usage is now simplified to the following:
- When used explicitly (e.g. through
MyClassMapper.fromMapormyClass.toMap()) no additional initialization is needed. - When used implicitly (through a generic type e.g.
MapperContainer.globals.fromMap<MyClass>()) the mapper needs to be initialized once before being used withMyClassMapper.ensureInitialized().
- When used explicitly (e.g. through
-
Breaking: Changed internal mapper implementation which causes any custom mapper to break.
- Removed
MapperElementBaseclass. - Added
MappingContextbeing passed to mapper methods.
See docs on how to use custom mappers in v3.
- Removed
-
Breaking: Removed
@MappableLib.createCombinedContainerin favor of@MappableLib.generateInitializerForScope.Instead of generating a new container, v3 generates an initialization function for all mappers. Use it early on in your application:
@MappableLib(generateInitializerForScope: InitializerScope.package) library main; import 'main.init.dart'; void main() { initializeMappers(); ... } -
Breaking: Improved support and features for
.copyWith.- Copy-With now supports classes that implement multiple interfaces.
- Renamed
.copyWith.apply()method to.copyWith.$update(). - Added
.copyWith.$merge()and.copyWith.$delta().
You can now use
.copyWithwith either an existing instance using.$mergeor a map of values using.$delta.@MappableClass() class A with AMappable { A(this.a, this.b); int? a; int? b; } void main() { var a = A(1, null); var c = a.copyWith.$merge(A(null, 2)); assert(c == A(1, 2)); var d = a.copyWith.$delta({'b': 2}); assert(d == A(1, 2)); } -
Breaking: Removed
CheckTypesHookin favor of discriminator functions.You can now use a custom predicate function as the
discriminatorValueof a class. This function can check whether the encoded value should be decoded to this subclass and return a boolean.@MappableClass() abstract class A with AMappable { A(); } @MappableClass(discriminatorValue: B.checkType) class B extends A with BMappable { B(); /// checks if [value] should be decoded to [B] static bool checkType(value) { return value is Map && value['isB'] == true; } } @MappableClass(discriminatorValue: C.checkType) class C extends A with CMappable { C(); /// checks if [value] should be decoded to [C] static bool checkType(value) { return value is Map && value['isWhat'] == 'C'; } } -
Added support for serializing fields that are not part of the constructor when annotated with
@MappableField(). -
Added
EncodingOptionstotoValuemethod. -
Added support for third-party models by using annotated
typedefs. -
Added
renameMethodsto build options. -
Improved performance of generated encoding and decoding methods.
For a detailed migration guide, see this issue.
-
-
3.0.0-dev.505 May 2023 pre-releaseNothing published for this version
-
3.0.0-dev.405 May 2023 pre-releaseNothing published for this version
-
3.0.0-dev.315 Apr 2023 pre-releaseNothing published for this version
-
3.0.0-dev.213 Apr 2023 pre-releaseNothing published for this version
-
3.0.0-dev.110 Mar 2023 pre-releaseNothing published for this version
-
3.0.0-dev.025 Feb 2023 pre-releaseNothing published for this version
-
2.0.316 Feb 2023Release notes
Open source →- Fixed typo in readme (by @timmaffett)
- Fixed generated container for private libraries.
-
2.0.201 Feb 2023 -
2.0.114 Jan 2023 -
2.0.011 Jan 2023Release notes
Open source →-
Mappers are now generated for each file containing annotated classes. This removes the
need to specify entry points in thebuild.yaml.This is now similar to how packages like
json_serializableorfreezedgenerate code.- Generated files are now
partfiles and need to be included as such. - All annotated classes must now use their respective
<MyClass>Mappablemixin. - Instead of one global
Mappereach class has its own<ClassName>Mapper.- A new global container that includes all models can now be generated using
@MappableLib(createCombinedContainer: true).
- A new global container that includes all models can now be generated using
- Mappers can be linked together to enable working with multiple classes.
- Removed
@CustomMapperannotation in favor ofincludeCustomMappersproperty on@MappableClass().
For a detailed migration guide, see this issue.
- Generated files are now
-
Documentation is now separated from the README using the official pub.dev documentation topics.
Find the new documentation here -
Improvements in performance and support for generics and inheritance.
-
Added the [CheckTypesHook] to allow for custom discriminator checks on subclasses in a
polymorphic class structure. -
CopyWith is now more powerful and also works for generic or polymorphic classes, while being
completely type-safe.When called on a superclass, the concrete subtype will be retained through a
.copyWithcall, which also respects generics:// with `class A` and `class B<T> extends A` A a = B<int>(); // static type A, dynamic type B<int> // signature will be `A copyWith()`, so static type A A a2 = a.copyWith(); // this will still resolve to a dynamic type of B<int> assert(a2 is B<int>);
Release notes
Open source →-
Mappers are now generated for each file containing annotated classes. This removes the need to specify entry points in the
build.yaml.This is now similar to how packages like
json_serializableorfreezedgenerate code.- Generated files are now
partfiles and need to be included as such. - All annotated classes must now use their respective
<MyClass>Mappablemixin. - Instead of one global
Mappereach class has its own<ClassName>Mapper.- A new global container that includes all models can now be generated using
@MappableLib(createCombinedContainer: true).
- A new global container that includes all models can now be generated using
- Mappers can be linked together to enable working with multiple classes.
- Removed
@CustomMapperannotation in favor ofincludeCustomMappersproperty on@MappableClass().
For a detailed migration guide, see this issue.
- Generated files are now
-
Documentation is now separated from the README using the official pub.dev documentation topics. Find the new documentation here
-
Improvements in performance and support for generics and inheritance.
-
Added the [CheckTypesHook] to allow for custom discriminator checks on subclasses in a polymorphic class structure.
-
CopyWith is now more powerful and also works for generic or polymorphic classes, while being completely type-safe.
When called on a superclass, the concrete subtype will be retained through a
.copyWithcall, which also respects generics:// with `class A` and `class B<T> extends A` A a = B<int>(); // static type A, dynamic type B<int> // signature will be `A copyWith()`, so static type A A a2 = a.copyWith(); // this will still resolve to a dynamic type of B<int> assert(a2 is B<int>);
-
-
2.0.0-dev.1310 Jan 2023 pre-releaseNothing published for this version
-
2.0.0-dev.1209 Jan 2023 pre-releaseNothing published for this version
-
2.0.0-dev.1105 Jan 2023 pre-releaseNothing published for this version
-
2.0.0-dev.1004 Jan 2023 pre-releaseNothing published for this version
-
2.0.0-dev.904 Jan 2023 pre-releaseNothing published for this version
-
2.0.0-dev.802 Jan 2023 pre-releaseNothing published for this version
-
2.0.0-dev.702 Jan 2023 pre-releaseNothing published for this version
-
2.0.0-dev.602 Dec 2022 pre-releaseNothing published for this version
-
2.0.0-dev.528 Oct 2022 pre-releaseNothing published for this version
-
2.0.0-dev.424 Oct 2022 pre-releaseNothing published for this version
-
2.0.0-dev.324 Oct 2022 pre-releaseNothing published for this version
-
2.0.0-dev.218 Oct 2022 pre-releaseNothing published for this version
-
2.0.0-dev.115 Oct 2022 pre-releaseNothing published for this version
-
2.0.0-dev.030 Sep 2022 pre-releaseNothing published for this version
-
1.2.008 Jun 2022 -
1.1.203 May 2022Release notes
Open source →- Improved Readme for constructor utilization
- Fixed missing imports for custom hooks
-
1.1.126 Apr 2022 -
1.1.011 Apr 2022Release notes
Open source →- Added support for custom enum values
- Choose between
ValuesMode.namedandValuesMode.indexedfor automatic generation of encoded values - Use
@MappableValue(myCustomValue)on an enum value to specify a custom encoded value - Deprecated the
String toStringValue()extension method in favor of the more generaldynamic toValue()
- Choose between
- Added support for custom enum values
-
1.0.306 Apr 2022 -
1.0.229 Mar 2022 -
1.0.129 Mar 2022Release notes
Open source →- Improved resolving of constructor parameters, which fixed various issues
in
copyWithandtoMapmethods - Now printing comprehensive warning in the builder output if a parameter cannot be resolved
- Improved resolving of constructor parameters, which fixed various issues
in
-
1.0.028 Mar 2022Release notes
Open source →-
Large refactoring and restructuring
- Restructured builder implementation
- Moved code out of generated files into package
- Split package int
dart_mappableanddart_mappable_builder
-
Mapperis now a singleton classfromValue,toValueand all other methods are now instance methods accessible withMapper.i.fromValue()- the legacy static methods are still available, but forward to the instance methods
-
Improved Readme, added How to use section and documented
copyWithfunctionalities -
Added
@MappableLib()annotation and moved library-level configuration logic from build options to annotation properties -
Switched to using the
type_pluspackage for internal handling of generics -
Added
ChainedHooks,UnescapeNewlinesHooksandEmptyToNullHooks -
Added descriptive and comprehensible exception handling
-
-
1.0.0+128 Mar 2022Nothing published for this version
-
1.0.0+228 Mar 2022Nothing published for this version
-
1.0.0-dev.728 Mar 2022 pre-releaseNothing published for this version