omni_mapper 0.5.2 copy "omni_mapper: ^0.5.2" to clipboard
omni_mapper: ^0.5.2 copied to clipboard

Annotations for the OmniMapper code generator. Automatically generates type-safe object-to-object mapping code between DTOs, Models, and Entities.

OmniMapper #

pub package License

A powerful, highly customizable code-generation library for Dart and Flutter that automatically generates object-to-object mapping code.

OmniMapper eliminates the boilerplate of manually writing conversion methods between your application layers (e.g., ModelEntity, DTOViewModel), keeping your codebase clean and reducing bugs.

Think of it as the AutoMapper/MapStruct for the Dart ecosystem.

Installation #

Add the following to your pubspec.yaml:

dependencies:
  omni_mapper: ^0.1.0

dev_dependencies:
  build_runner: ^2.4.0
  omni_mapper_generator: ^0.1.0

Then run:

dart pub get

Quick Start #

1. Annotate your class #

import 'package:omni_mapper/omni_mapper.dart';

part 'user_model.g.dart';

class UserEntity {
  final int id;
  final String name;
  UserEntity({required this.id, required this.name});
}

@OmniMapper(target: UserEntity)
class UserModel {
  final int id;
  final String name;
  UserModel({required this.id, required this.name});
}

2. Run the generator #

dart run build_runner build -d

3. Use the generated code #

final model = UserModel(id: 1, name: 'John');
final entity = model.toEntity(); // Automatically mapped!

Mapping Approaches #

OmniMapper supports three mapping strategies to fit your architecture:

Approach A: Abstract Class (Centralized Mapper) #

@OmniMapper()
abstract class UserMapper {
  UserEntity toEntity(UserModel model);
}
// Generates: class UserMapperImpl extends UserMapper { ... }

Approach B: Extension TO Target #

@OmniMapper(target: UserEntity)
class UserModel { ... }
// Generates: extension on UserModel { UserEntity toEntity() { ... } }

Approach C: Extension FROM Source #

@OmniMapper(from: UserEntity, methodName: 'toModel')
class UserModel { ... }
// Generates: extension on UserEntity { UserModel toModel() { ... } }

Multiple Mappings #

Map a single class to multiple targets using @OmniMappers:

@OmniMappers([
  OmniMapper(target: UserEntity),
  OmniMapper(from: UserEntity, methodName: 'toModel'),
])
class UserModel { ... }

Advanced Features #

Custom Field Mapping #

When source and target have different property names, you have two options depending on your control over the classes:

Place @OmniField directly on the property. This is the most ergonomic approach because the mapping rule stays right next to the variable declaration.

@OmniMapper(target: UserEntity)
class UserModel {
  @OmniField(name: 'id') // Maps 'userId' to 'id'
  final int userId;
  // ...
}

If you cannot modify the class (e.g., it belongs to a third-party package or is generated code), or if you need advanced custom expressions, use MappingRule inside @OmniMapper.

@OmniMapper(
  target: UserEntity,
  mappings: [
    MappingRule('id', source: 'userId'), // source.userId → target.id
  ],
)
class UserModel {
  final int userId;
  // ...
}

Default Values #

Provide fallback values for target fields missing in the source:

Option 1: @OmniField

class UserModel {
  @OmniField(defaultValue: '"active"')
  final String status;
}

Option 2: mappings

@OmniMapper(
  target: UserEntity,
  mappings: [
    MappingRule('status', defaultValue: '"active"'),
    MappingRule('createdAt', defaultValue: 'DateTime.now()'),
  ],
)

Custom Type Converters #

Handle type mismatches with OmniConverter:

class DateTimeStringConverter extends OmniConverter<String, DateTime> {
  const DateTimeStringConverter();

  @override
  DateTime convert(String source) => DateTime.parse(source);
}

@OmniMapper(
  target: UserEntity,
  converters: [DateTimeStringConverter],
)
class UserModel {
  final String createdAt; // String → DateTime automatically
}

List Generation #

Automatically generates an extension on Iterable<Source> for batch mapping:

final models = [model1, model2, model3];
final entities = models.toEntityList(); // Returns List<UserEntity>

Enabled by default. Disable with generateListMapper: false.

In-Place Updates #

Generates a method to update an existing target instance without creating a new one:

final existingEntity = UserEntity(id: 1, name: 'Old');
formModel.updateUserEntity(existingEntity);
// existingEntity.name is now updated — same object in memory!

Disabled by default. Enable with generateUpdateMethod: true. Works with mutable fields only (non-final).

Ignoring Fields #

Skip specific fields during mapping:

Option 1: @OmniField

class UserModel {
  @OmniField(ignore: true)
  final String passwordHash;
}

Option 2: mappings

@OmniMapper(
  target: UserEntity,
  mappings: [
    MappingRule('passwordHash', ignore: true),
  ],
)

Polymorphic Mapping (@SubclassMapping) #

Handle subclasses and inheritance dynamically based on the runtime type of the source object:

@OmniMapper(
  target: BaseEntity,
  subclasses: [
    SubclassMapping(source: AdminModel, target: AdminEntity),
    SubclassMapping(source: GuestModel, target: GuestEntity),
  ]
)
class BaseModel { ... }

Dependency Injection & Composition (uses) #

Reference existing mappers to handle complex nested fields automatically. The generated implementation will ask for these mappers in its constructor:

@OmniMapper()
abstract class AddressMapper {
  AddressEntity toEntity(AddressModel model);
}

@OmniMapper(uses: [AddressMapper])
abstract class UserMapper {
  final AddressMapper addressMapper;
  UserMapper(this.addressMapper);

  UserEntity toEntity(UserModel model);
}

Custom Field Expressions (MappingRule.custom) #

Use pure Dart code for extreme flexibility when mapping fields:

@OmniMapper(
  target: UserEntity,
  mappings: [
    MappingRule('fullName', custom: 'firstName + " " + lastName'),
  ],
)
class UserModel { ... }

Multiple Sources Mapping #

Combine multiple source objects into a single target object by defining a centralized mapper:

@OmniMapper()
abstract class UserProfileMapper {
  UserProfile toProfile(User user, Address address);
}

Automatic Enum Mapping #

Enums are mapped automatically if their values match by name. No additional configuration is required.

Deep Auto-Flattening #

Nested objects are automatically flattened if the target fields match the nested structure (e.g., source.user.address.street maps to target.street).

Strict Mode & IgnoreIfNull #

Enforce complete mappings and support PATCH-like updates:

@OmniMapper(
  target: UserEntity,
  strictMode: true, // Throws an error if any target field is unmapped
  ignoreIfNull: true, // Null source fields won't overwrite target fields (useful for updates)
)
class UserModel { ... }

To suppress lint warnings on generated files, add this to your project's build.yaml:

targets:
  $default:
    builders:
      source_gen|combining_builder:
        options:
          ignore_for_file:
            - type=lint
            - coverage:ignore-file

Running the Generator #

# One-time build
dart run build_runner build -d

# Watch mode (rebuilds on file changes)
dart run build_runner watch -d

Contributing #

Contributions are welcome! Please file issues and pull requests on the GitHub repository.

License #

This project is licensed under the BSD 3-Clause License — see the LICENSE file for details.

1
likes
160
points
141
downloads

Documentation

API reference

Publisher

verified publishermveloso.dev

Weekly Downloads

Annotations for the OmniMapper code generator. Automatically generates type-safe object-to-object mapping code between DTOs, Models, and Entities.

Repository (GitHub)
View/report issues

Topics

#mapper #code-generation #clean-architecture #dto

License

BSD-3-Clause (license)

More

Packages that depend on omni_mapper