easy_copy_with 2.0.0
easy_copy_with: ^2.0.0 copied to clipboard
Package for automatic generation of copyWith methods in Dart classes using @CopyWith annotation.
Easy CopyWith #
A lightweight Dart package for automatic generation of copyWith
methods using the @CopyWith
annotation.
Features #
- π Simple annotation-based API
- β‘ Build-time code generation (no runtime overhead)
- π Supports nullable and non-nullable fields
- π― Compatible with correct type handling
- π¦ Minimal dependencies
- π οΈ Easy integration with existing projects
Installation #
Add to your pubspec.yaml
:
dependencies:
easy_copy_with: ^1.0.0
dev_dependencies:
build_runner: ^2.3.3
Then run:
dart pub get
Quick Start #
- Import the package:
import 'package:easy_copy_with/easy_copy_with.dart';
- Add part directive:
part 'your_file.copy_with.dart';
- Annotate your class:
@CopyWith()
class Person {
final String name;
final int age;
final String? email;
const Person({
required this.name,
required this.age,
this.email,
});
}
- Generate code:
dart run build_runner build
- Use the generated copyWith method:
final person = Person(name: 'John', age: 30);
final olderPerson = person.copyWith(age: 31);
Example #
import 'package:easy_copy_with/easy_copy_with.dart';
part 'example.g.dart';
@CopyWith()
class User {
final String name;
final int age;
final String? email;
final bool isActive;
const User({
required this.name,
required this.age,
this.email,
this.isActive = true,
});
}
void main() {
const user = User(
name: 'Alice',
age: 25,
email: 'alice@example.com',
);
final older = user.copyWith(age: 26);
final renamed = older.copyWith(name: 'Alicia');
final withoutEmail = renamed.copyWith(email: null);
print(user);
print(older);
print(renamed);
print(withoutEmail);
}
You can run the complete working sample with dart run example/example.dart
.
How It Works #
The package uses source_gen
and build_runner
to analyze classes with the @CopyWith
annotation and generate extension methods at build time.
Code Generation Process #
- Analysis: The generator scans for classes annotated with
@CopyWith
- Validation: Ensures the class is not abstract and has a suitable constructor
- Field Detection: Identifies all non-static, non-synthetic fields
- Extension Generation: Creates a
copyWith
method as an extension
Generated Code Structure #
For the example above, the generated code looks like:
typedef UserCopyWithFn =
User Function({String? name, int? age, String? email, bool? isActive});
const Object _userCopyWithPlaceholder = Object();
extension UserCopyWith on User {
UserCopyWithFn get copyWith {
final instance = this;
User copyWithFn({
Object? name = _userCopyWithPlaceholder,
Object? age = _userCopyWithPlaceholder,
Object? email = _userCopyWithPlaceholder,
Object? isActive = _userCopyWithPlaceholder,
}) {
return User(
name: identical(name, _userCopyWithPlaceholder) || name == null
? instance.name
: name as String,
age: identical(age, _userCopyWithPlaceholder) || age == null
? instance.age
: age as int,
email: identical(email, _userCopyWithPlaceholder)
? instance.email
: email as String?,
isActive:
identical(isActive, _userCopyWithPlaceholder) || isActive == null
? instance.isActive
: isActive as bool,
);
}
return copyWithFn as UserCopyWithFn;
}
}
Nullable Field Handling #
The generator handles nullable and non-nullable fields differently:
- Non-nullable fields (e.g.,
String name
): Parameters remain type-safe (String? name
) while still preventing unintended null assignment in the generated body. - Nullable fields (e.g.,
String? email
): Internally use a sentinel value to distinguish between "don't change" and "set to null", allowing you to passnull
explicitly.
This approach allows you to explicitly set nullable fields to null
:
// Set email to null
final userWithoutEmail = user.copyWith(email: null);
// Keep existing email value
final userSameEmail = user.copyWith(name: 'New Name');
File Extensions #
By default, generated files use the .copy_with.dart
extension. You can customize this in your build.yaml
:
targets:
$default:
builders:
easy_copy_with:copy_with:
options:
output_extension: ".g.dart" # Use .g.dart instead
Supported Class Types #
The generator works with:
- β Regular classes with named constructors
- β Classes with generic type parameters
- β Classes with nullable and non-nullable fields
- β Abstract classes
- β Classes without suitable constructors
Commands #
# Generate code once
dart run build_runner build
# Watch for changes and rebuild automatically
dart run build_runner watch
# Clean generated files
dart run build_runner clean
Requirements #
- Dart SDK:
>=3.9.2
- Compatible with Flutter projects
Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
License #
This project is licensed under the MIT License β see the LICENSE file for details.