easy_copy_with 3.0.0
easy_copy_with: ^3.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: ^2.1.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.g.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
copyWithmethod 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 passnullexplicitly.
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 .g.dart extension, so the generated code happily shares the same part file with other source_gen builders (for example json_serializable or freezed).
If you prefer to keep a dedicated .copy_with.dart file, override the builder configuration in your projectβs build.yaml:
targets:
$default:
builders:
easy_copy_with:copy_with:
options:
output_extension: ".copy_with.dart" # Use .copy_with.dart instead
builders:
easy_copy_with:copy_with:
import: "package:easy_copy_with/src/builder.dart"
builder_factories: ["copyWithBuilder"]
build_extensions: {".dart": [".copy_with.dart"]}
auto_apply: dependents
build_to: source
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.