smart_mapper
Focused object mapping for Clean Architecture boundaries. It generates small, plain Dart mappers between Remote DTOs, Local Models, and Domain Entities.
Setup
Add runtime annotations and generator:
dependencies:
smart_mapper: ^0.2.0
dev_dependencies:
build_runner: ^2.16.0
smart_mapper_generator: ^0.2.0
Define an abstract mapper. Same-name, assignable properties map automatically. Declare renames and converters explicitly:
import 'package:smart_mapper/smart_mapper.dart';
part 'user_mapper.g.dart';
@SmartMapper()
abstract class UserMapper {
@MapField(
target: 'name',
source: 'fullName',
using: 'normalizeName',
)
User map(UserResponse source);
UserBox toLocal(UserResponse source);
User fromLocal(UserBox source);
}
String normalizeName(String value) => value.trim();
Generate code:
dart run build_runner build
Use generated factory:
final mapper = createUserMapper();
final user = mapper.map(response);
Generated implementation stays intentionally boring: constructor calls, property reads, and explicit converter calls.
Strict validation
Every direct assignment is checked with analyzer's type system. Invalid boundaries fail during generation instead of producing broken Dart:
Cannot assign String? to DateTime for Homework.dueDate.
Use a top-level converter when source and target types differ:
@MapField(target: 'createdAt', source: 'createdAt', using: 'parseDate')
Profile fromRemote(ProfileDto source);
DateTime parseDate(String value) => DateTime.parse(value);
Constructor controls
@MapConstructor('fromDto')
@MapIgnore('id')
@MapDefault(target: 'status', value: 'active')
Profile fromRemote(ProfileDto source);
MapDefault accepts constant null, bool, numbers, strings, lists, sets,
and maps.
Nested collections and ObjectBox ToMany
Declare item mapping methods beside parent mapping methods:
@SmartMapper()
abstract class ScheduleMapper {
Schedule fromRemote(ScheduleDto source);
ScheduleItem itemFromRemote(ScheduleItemDto source);
@MapRelation(
target: 'monday',
source: 'monday',
using: 'itemToLocal',
)
ScheduleBox toLocal(Schedule source);
ScheduleItemBox itemToLocal(ScheduleItem source);
Schedule fromLocal(ScheduleBox source);
ScheduleItem itemFromLocal(ScheduleItemBox source);
}
List, Set, and Iterable collections use matching item mappers. ObjectBox
relations require @MapRelation; no relation is mutated implicitly. A target
ToMany<TargetItem> field is populated after construction:
final target = ScheduleBox(group: source.group);
target.monday.addAll(source.monday.map(itemToLocal));
return target;
Missing or ambiguous item mapper methods fail during generation.
ObjectBox ToOne is also supported. Nullable targets remain nullable, with
converter calls guarded against null.
Scope
Version 0.2.0 supports synchronous methods with exactly one required
positional source parameter and target types with an unnamed generative or
factory constructor. Named constructors, nested List/Set/Iterable,
nullable collections/items, and ObjectBox ToMany/ToOne relations are
supported. Async and update-in-place mapping remain outside current scope.