maybe_copy_with 1.0.0
maybe_copy_with: ^1.0.0 copied to clipboard
A lightweight package that lets you write ergonomic `copyWith` methods WITHOUT any code generation or `build_runner`.
Maybe copyWith #
A lightweight package that lets you write ergonomic copyWith methods WITHOUT any code generation or build_runner.
maybe_copy_with fixes one pain only: how to tell “leave the field as‑is” from “explicitly set it to null” for nullable properties—while keeping the call site short and expressive.
📦 Installation #
flutter pub add maybe_copy_with
or add it manually to your pubspec.yaml:
dependencies:
maybe_copy_with: ^<latest_version>
💡 One‑shot overview #
import 'package:maybe_copy_with/maybe_copy_with.dart';
void main() {
// Original instance
final john = User(name: 'John', age: 18, gender: 'Male');
// 1️⃣ Change a non‑nullable field — `gender` stays untouched
final bob = john.copyWith(name: 'Bob');
// or
// final bob = john.copyWith(name: 'Bob', gender: null);
// 2️⃣ Change both a non‑nullable and a nullable field
final alice = bob.copyWith(
name: 'Alice',
gender: 'Female'.maybe(), // wrap with .maybe() to *replace* a nullable field
);
// 3️⃣ Explicitly reset the nullable field to null
final android = alice.copyWith(
gender: null.maybe(), // set to null via .maybe()
);
print(john); // User(name: John, age: 18, gender: Male)
print(bob); // User(name: Bob, age: 18, gender: Male)
print(alice); // User(name: Alice, age: 18, gender: Female)
print(android); // User(name: Alice, age: 18, gender: null)
}
class User with MaybeCopyWith<User> {
final String name;
final int age;
final String? gender;
const User({
required this.name,
required this.age,
this.gender,
});
/// Returns a **new** [User] where only the fields you specify are changed.
///
/// * `name`, `age` – plain nullable params. Pass a new value to replace the
/// current one, or omit (leave `null`) to keep the existing value.
/// * `gender` – wrapped in [Maybe] so you have **three** options:
/// * `null` or omit → keep current `gender` value;
/// * `'Female'.maybe()` → set a new non‑null value;
/// * `null.maybe()` → explicitly reset to `null`.
@override
User copyWith({
String? name,
int? age,
Maybe<String>? gender,
}) {
return User(
name: name ?? this.name,
age: age ?? this.age,
gender: gender.or(this.gender),
);
}
@override
String toString() => 'User(name: $name, age: $age, gender: $gender)';
}
Why this API? #
| Scenario | Call site |
|---|---|
| keep field as is | gender: null or omit the parameter |
| replace with a new value | gender: 'Alice'.maybe() |
reset to null (for T?) |
gender: null.maybe() |
For non‑nullable fields T, passing null already means “leave it unchanged”, so no wrapper is needed.
🔑 API essentials #
/// Wrapper that marks a value as “explicitly provided”.
typedef Maybe<T>;
/// Converts a value to Maybe.
/// `null` goes through untouched, so `null.maybe()` returns `null`.
Maybe<T> maybe();
/// Returns the wrapped value, or [fallback] if the Maybe is `null`.
T? or(T? fallback);
MaybeCopyWith<T> mixin #
Adds a single signature T copyWith(); so your IDE will remind you to implement it.
✨ Highlights #
- Zero dependencies — pure Dart, works on Mobile, Web and Desktop.
- No
build_runner— lack of code generation.
🚧 Limitations #
- Fields must still be listed manually — there is no reflection (
dart:mirrorsis unavailable on Flutter) and macros are still in IceBox. - Nullable fields require
Maybe<T>?in thecopyWithparameters.
📝 Changelog #
Please see the Changelog page to know what's recently changed.
🤝 Contributions #
Feel free to contribute to this project.
If you find a bug or want a feature, but don't know how to fix/implement it, please fill an issue. If you fixed a bug or implemented a feature, please send a pull request.