easy_copy_with 2.0.0 copy "easy_copy_with: ^2.0.0" to clipboard
easy_copy_with: ^2.0.0 copied to clipboard

Package for automatic generation of copyWith methods in Dart classes using @CopyWith annotation.

Easy CopyWith #

pub package License

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 #

  1. Import the package:
import 'package:easy_copy_with/easy_copy_with.dart';
  1. Add part directive:
part 'your_file.copy_with.dart';
  1. 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,
  });
}
  1. Generate code:
dart run build_runner build
  1. 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 #

  1. Analysis: The generator scans for classes annotated with @CopyWith
  2. Validation: Ensures the class is not abstract and has a suitable constructor
  3. Field Detection: Identifies all non-static, non-synthetic fields
  4. 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 pass null 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.

2
likes
150
points
194
downloads

Publisher

unverified uploader

Weekly Downloads

Package for automatic generation of copyWith methods in Dart classes using @CopyWith annotation.

Repository (GitHub)
View/report issues

Topics

#codegen #copy-with #code-generation #annotation

Documentation

API reference

License

MIT (license)

Dependencies

analyzer, build, meta, source_gen

More

Packages that depend on easy_copy_with