Skip to content

Latest commit

Β 

History

449 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Zorphy

Zorphy is a powerful code generation package for Dart/Flutter that provides clean, immutable class definitions with advanced features including copyWith methods, JSON serialization, equality, toString, inheritance support, and sophisticated patch mechanisms.

Sponsor

https://zuzu.dev Sponsored by ZikZak AI

Thanks to ZikZak AI for sponsoring this project!

ZikZak AI is an AI-Powered Price Comparison app that you scan barcodes, and discover amazing savings instantly. Your personal shopping assistant that never sleeps.

πŸ“– Documentation

Visit the official documentation at arrrrny.github.io/zorphy for detailed guides, examples, and API references.

You can also find the package on pub.dev.

✨ Features

  • πŸ“‹ Immutable Data Classes - Clean, immutable class definitions with minimal boilerplate
  • πŸ”„ CopyWith Methods - Generate copyWith methods for creating modified copies
  • 🏷️ Field-Selector CopyWith - copyWithField(Field<E, T> field, T value) replaces a single field via the typed Field selector; passing null to a nullable field preserves the current value
  • 🎯 Function-based CopyWith - Optional function-based copyWith for computed updates
  • πŸ”§ Patch System - Advanced patching mechanism for partial updates with nested support
  • πŸ“¦ JSON Serialization - Full toJson/fromJson support with polymorphic type handling
  • βš–οΈ Equality - Auto-generated == operator and hashCode
  • πŸ“ toString - Meaningful string representations for debugging
  • πŸ” Sealed Classes - Support for sealed classes with exhaustiveness checking
  • 🌳 Self-Referencing Types - Handle tree structures and hierarchical data
  • 🏭 Factory Methods - Support for custom factory constructors
  • 🧬 Inheritance & Polymorphism - Multiple inheritance, generics, and interfaces
  • πŸ”’ Enum Support - Full enum integration with JSON serialization
  • πŸ“Š CompareTo - Generate comparison methods showing differences between instances
  • πŸ”„ ChangeTo - Convert between related types in inheritance hierarchies
  • πŸ€– AI-Friendly CLI - Command-line tool optimized for AI agents and developers
  • πŸ”Œ MCP Server - Model Context Protocol server for agentic integration

βš–οΈ Zorphy vs Freezed

Both generate immutable data classes. Zorphy goes further β€” it is a full entity toolkit with a nested patch system, filter/query descriptors, compareTo diffs, and an AI-agent CLI/MCP server.

Capability zorphy 2.0 freezed 3.2.5
Immutable data classes + copyWith βœ… βœ…
Function-based copyWith (copyWithFn) βœ… opt-in (ZorphyPreset.full) ❌
JSON + polymorphic discriminators + toJsonLean βœ… βœ… (no toJsonLean)
Sealed unions βœ… sealed $$Base + Dart 3 pattern matching βœ… via when/map helpers
Nested patch system (partial updates of deep graphs) βœ… UserPatch()..withAddressPatch(...) ❌ manual nested copyWith chains
Filter/query descriptors (Field<E, T>) βœ… ❌
compareTo diffs between instances βœ… ❌
changeTo conversions between subtypes βœ… ❌
Multiple interface inheritance with generics βœ… ❌
Self-referencing types βœ… βœ…
CLI + MCP server for scaffolding βœ… ❌
Output size control βœ… ZorphyPreset.lean/standard/full + per-feature flags ❌
analyzer 14 support βœ… >=13.0.0 <15.0.0 ❌ caps analyzer <11.0.0 (as of 2026-07-30)

Where freezed differs (honest notes): freezed's mixin pattern (with _$Foo) keeps the annotated class itself concrete; zorphy uses abstract class $Foo + generated Foo. freezed's when/map helpers are answered in zorphy by Dart 3's native exhaustive switch on the sealed base. Freezed's ecosystem is older and larger.

Side by side

1. Simple data class β€” freezed:

@freezed
class User with _$User {
  const factory User({required String id, required String name, String? email}) = _User;
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

zorphy (lean preset β€” no patch/filter/compareTo bloat):

@Zorphy(preset: ZorphyPreset.lean, generateJson: true)
abstract class $User {
  String get id;
  String get name;
  String? get email;
}

2. Sealed union β€” freezed:

@freezed
class Result with _$Result {
  const factory Result.ok(String value) = Ok;
  const factory Result.err(String message) = Err;
}

zorphy (real sealed hierarchy, exhaustive Dart 3 switch):

@Zorphy(explicitSubTypes: [$Ok, $Err])
abstract class $$Result {}

@Zorphy()
abstract class $Ok implements $$Result { String get value; }

@Zorphy()
abstract class $Err implements $$Result { String get message; }

String describe(Result r) => switch (r) {
  Ok(:final value) => 'ok: $value',
  Err(:final message) => 'err: $message', // exhaustive β€” no default needed
};

3. Nested partial update β€” freezed requires manual deep copyWith:

user.copyWith(address: user.address.copyWith(city: 'Berlin'));

zorphy's patch system composes:

final patch = ProfilePatch()
  ..withName('Ada')
  ..withAddressPatch(AddressPatch()..withCity('Berlin'));
final updated = patch.applyTo(profile);

All three zorphy examples are real, compilable code β€” see zorphy/example/lib/comparison/.

Migrating from freezed? Use the zorphy_migrator codemod:

dart pub global activate zorphy_migrator
zorphy_migrator migrate lib/ --dry-run    # preview unified diff
zorphy_migrator migrate lib/ --apply --report MIGRATION.md

It converts simple classes, sealed unions, @Default, @JsonKey, and fromJson/toJson automatically, and reports anything that needs human attention with file:line β€” nothing is silently dropped. See the migration guide.

πŸ“¦ Installation

Add the dependencies to your pubspec.yaml:

dependencies:
  zorphy_annotation: ^2.4.3

dev_dependencies:
  zorphy: ^2.4.3
  build_runner: ^2.4.0

Install the CLI globally:

dart pub global activate zorphy

πŸš€ Quick Start

Using the CLI (Recommended for AI Agents)

The Zorphy CLI is designed for optimal AI agent usage:

# Create entity
zorphy create -n User

# Create with fields
zorphy create -n Product \
  --field name:String \
  --field price:double \
  --field inStock:bool

# Quick create (simple entity with defaults)
zorphy new -n Category

# Build all entities
zorphy build

# List entities
zorphy list

Manual Class Definition

Or create classes manually:

import 'package:zorphy_annotation/zorphy_annotation.dart';

part 'user.zorphy.dart';

@Zorphy()
abstract class $User {
  String get name;
  int get age;
  String? get email;
}

Run the generator:

dart run build_runner build

Use the generated class:

void main() {
  final user = User(name: 'Alice', age: 30);

  // Copy with changes
  final olderUser = user.copyWith(age: 31);

  // Convert to JSON (if generateJson: true)
  final json = user.toJson();

  // Create from JSON
  final fromJson = User.fromJson(json);
}

πŸ€– Zorphy CLI

The Zorphy CLI provides an intuitive interface for creating and managing entities, optimized for both human developers and AI agents.

Installation

# Install globally
dart pub global activate zorphy

# Or run directly
dart run zorphy:zorphy_cli

CLI Commands

create - Create New Entity

Create a new Zorphy entity with full control over options:

zorphy create [options]

Options:

  • -n, --name - Entity name (required)
  • -o, --output - Output directory (default: lib/src/domain/entities)
  • -p, --package - Package name for imports
  • --json - Enable JSON serialization (default: true)
  • --copywith-fn - Enable function-based copyWith (default: false)
  • --compare - Enable compareTo (default: true)
  • --filter - Enable filter descriptors (default: false)
  • --sealed - Create sealed class (default: false)
  • --non-sealed - Create non-sealed class (default: false)
  • --field - Add fields directly (name:type or name:type?), repeatable
  • --extends - Interface to extend (e.g., $BaseEntity)
  • --subtypes - Explicit subtypes for polymorphism (repeatable)
  • --generate-subs - Auto-generate subtype stubs for sealed classes
  • --dry-run - Preview generated code without writing files

Examples:

# Create with fields
zorphy create -n User \
  --field name:String \
  --field age:int \
  --field email:String?

# With all options
zorphy create -n Product \
  --output lib/models \
  --json \
  --compare \
  --field name:String \
  --field price:double \
  --field category:String?

# Sealed class with subtypes
zorphy create -n Result --sealed \
  --subtypes Ok \
  --subtypes Err

# Dry run (preview only)
zorphy create -n User --field name:String --dry-run

# With inheritance
zorphy create -n Admin \
  --extends '$User' \
  --field permissions:List<String>

new - Quick Create

Create a simple entity with default settings:

zorphy new -n EntityName

Options:

  • -n, --name - Entity name (required)
  • -o, --output - Output directory (default: lib/src/domain/entities)
  • --json - Enable JSON (default: true)

Example:

zorphy new -n Product

build - Run Code Generation

Generate code for all Zorphy entities:

zorphy build [options]

Options:

  • -w, --watch - Watch for changes (default: false)
  • -c, --clean - Clean before build (default: false)
  • --dry-run - Preview without writing (default: false)
  • --force - Force rebuild even if up to date (default: false)

Examples:

# Build once
zorphy build

# Clean and build
zorphy build --clean

# Watch mode
zorphy build --watch

# Force rebuild
zorphy build --force

list - List Entities

List all Zorphy entities in a directory:

zorphy list [options]

Options:

  • -o, --output - Directory to search (default: lib/src/domain/entities)

Example:

zorphy list
# Output:
# πŸ“‚ Zorphy Entities in lib/src/domain/entities:
#
# πŸ“„ User
#    File: lib/src/domain/entities/user.dart
#    βœ“ JSON support
#    βœ“ Function-based copyWith
#
# Total: 1 entity/entities

Field Types

The CLI supports various field types:

  • Basic types: String, int, double, bool, num, DateTime
  • Nullable types: Add ? after type (e.g., String?, int?)
  • Generic types: List<Type>, Set<Type>, Map<KeyType, ValueType>
  • Custom types: Any other class name

Examples:

--field name:String
--field age:int
--field email:String?
--field tags:List<String>
--field metadata:Map<String, dynamic>
--field createdAt:DateTime

enum - Create Enum

Create a new Zorphy-compatible enum:

zorphy enum -n Status --value active,inactive,pending

Options:

  • -n, --name - Enum name (required)
  • --value - Comma-separated enum values (required)
  • -o, --output - Output directory (default: lib/src/domain/entities)

Example:

zorphy enum -n UserRole --value admin,user,guest

add-field - Add Field to Existing Entity

Add a field to an existing Zorphy entity file:

zorphy add-field -n User -f email:String?

Options:

  • -n, --name - Entity name (required)
  • -f, --field - Field to add (name:type or name:type?) (required)

Example:

zorphy add-field -n User -f avatarUrl:String?
zorphy add-field -n Product -f tags:List<String>

watch - Watch and Rebuild

Watch for file changes and automatically rebuild:

zorphy watch

from-json - Create Entity from JSON

Generate a Zorphy entity from a JSON sample:

zorphy from-json -n User --json-input '{"name": "Alice", "age": 30}'

Options:

  • -n, --name - Entity name (required)
  • --json-input - JSON string to generate from (required)
  • -o, --output - Output directory (default: lib/src/domain/entities)

πŸ”Œ Zorphy MCP Server

The Model Context Protocol (MCP) server enables AI agents like Claude to programmatically create and manage Zorphy entities.

Setting up the MCP Server

Add to your Claude/MCP client configuration:

{
  "mcpServers": {
    "zorphy": {
      "command": "zorphy_mcp_server"
    }
  }
}

The zorphy_mcp_server executable is registered when you run dart pub global activate zorphy. Alternatively, use dart run zorphy:zorphy_mcp_server.

Available MCP Tools

create_entity

Create a new Zorphy entity programmatically.

Parameters:

{
  "name": "string (required)",
  "outputDir": "string (default: lib/src/domain/entities)",
  "package": "string (optional)",
  "generateJson": "boolean (default: true)",
  "generateCompareTo": "boolean (default: true)",
  "sealed": "boolean (default: false)",
  "nonSealed": "boolean (default: false)",
  "extends": "string (optional)",
  "explicitSubTypes": ["string"],
  "fields": [
    {
      "name": "string",
      "type": "string"
    }
  ]
}

Note: Parameters are flat (no nested options object). Fields use the ? suffix in the type string for nullable fields (e.g., "type": "String?").

Example:

{
  "name": "User",
  "fields": [
    { "name": "id", "type": "String" },
    { "name": "name", "type": "String" },
    { "name": "email", "type": "String?" },
    { "name": "age", "type": "int" }
  ],
  "generateJson": true,
  "generateCompareTo": true
}

create_enum

Create a new Zorphy-compatible enum.

Parameters:

{
  "name": "string (required)",
  "values": ["string (required)"],
  "outputDir": "string (default: lib/src/domain/entities)"
}

Example:

{
  "name": "UserRole",
  "values": ["admin", "user", "guest"]
}

add_field

Add a field to an existing Zorphy entity.

Parameters:

{
  "name": "string (required) β€” entity name",
  "fields": [
    {
      "name": "string (required)",
      "type": "string (required)"
    }
  ]
}

Example:

{
  "name": "User",
  "fields": [{ "name": "avatarUrl", "type": "String?" }]
}

list_entities

List all Zorphy entities in a directory.

Parameters:

{
  "directory": "string (default: lib/src/domain/entities)"
}

Agentic Usage Examples

Example 1: Agent Creating a User Entity

# Agent using MCP server
mcp.call_tool("create_entity", {
    "name": "User",
    "fields": [
        {"name": "id", "type": "String"},
        {"name": "username", "type": "String"},
        {"name": "email", "type": "String?"},
        {"name": "createdAt", "type": "DateTime"}
    ],
    "generateJson": True,
    "generateCompareTo": True
})

Example 2: Agent Creating a Sealed Hierarchy

# Create the sealed base
mcp.call_tool("create_entity", {
    "name": "PaymentMethod",
    "sealed": True,
    "explicitSubTypes": ["$CreditCard", "$PayPal"]
})

# Create each variant
mcp.call_tool("create_entity", {
    "name": "CreditCard",
    "extends": "$$PaymentMethod",
    "fields": [
        {"name": "cardNumber", "type": "String"},
        {"name": "expiryDate", "type": "String"}
    ]
})

mcp.call_tool("create_entity", {
    "name": "PayPal",
    "extends": "$$PaymentMethod",
    "fields": [
        {"name": "email", "type": "String"}
    ]
})

Example 3: Agent Adding a Field and Listing Entities

# List existing entities
entities = mcp.call_tool("list_entities", {})

# Add a field to an existing entity
mcp.call_tool("add_field", {
    "name": "User",
    "fields": [{"name": "avatarUrl", "type": "String?"}]
})

# Create an enum
mcp.call_tool("create_enum", {
    "name": "UserRole",
    "values": ["admin", "user", "guest"]
})

πŸ“– Complete Feature Guide

1. Class Definition Patterns

Concrete Classes (Single $)

Use a single $ prefix for concrete classes:

@Zorphy()
abstract class $Person {
  String get firstName;
  String get lastName;
  int? get age;
}

// Usage: Omit the $ when using
final person = Person(firstName: 'John', lastName: 'Doe');

Sealed Abstract Classes (Double $$)

Use $$ prefix for sealed abstract classes. Sealed classes enable exhaustiveness checking:

@Zorphy()
abstract class $$Shape {
  double get area;
}

@Zorphy()
abstract class $Circle implements $$Shape {
  double get radius;

  @override
  double get area => 3.14159 * radius * radius;
}

@Zorphy()
abstract class $Rectangle implements $$Shape {
  double get width;
  double get height;

  @override
  double get area => width * height;
}

// Exhaustiveness checking
String describeShape(Shape shape) => switch (shape) {
  Circle() => 'A circle',
  Rectangle() => 'A rectangle',
};

Non-Sealed Abstract Classes

Use nonSealed: true to create non-sealed abstract classes:

@Zorphy(nonSealed: true)
abstract class $$BaseEntity {
  String get id;
  DateTime get createdAt;
}

2. JSON Serialization

Enable JSON generation with generateJson: true:

@Zorphy(generateJson: true)
abstract class $User {
  String get id;
  String get name;
  String? get email;
}

Features:

  • Automatic toJson() and fromJson()
  • toJsonLean() removes metadata for cleaner output
  • Handles nested objects and collections
  • Supports polymorphic serialization with type discriminators

Polymorphic JSON

@Zorphy()
abstract class $$Animal {}

@Zorphy(generateJson: true)
abstract class $Dog implements $$Animal {
  String get breed;
}

@Zorphy(generateJson: true)
abstract class $Cat implements $$Animal {
  double get whiskerLength;
}

// Serialization includes type discriminator
final dog = Dog(breed: 'Labrador');
final json = dog.toJson();
// {"__typename": "Dog", "breed": "Labrador"}

// Deserialization automatically handles type
final animal = Animal.fromJson(json);
print(animal); // Instance of Dog

3. CopyWith Methods

Basic CopyWith

@Zorphy()
abstract class $User {
  String get name;
  int get age;
}

final user = User(name: 'Alice', age: 30);
final updated = user.copyWith(name: 'Bob');
// User(name: 'Bob', age: 30)

Function-based CopyWith

Enable with generateCopyWithFn: true:

@Zorphy(generateCopyWithFn: true)
abstract class $Counter {
  int get value;
}

final counter = Counter(value: 0);
final incremented = counter.copyWithCounterFn(
  value: () => counter.value + 1,
);
// Counter(value: 1)

Type-specific CopyWith

@Zorphy()
abstract class $Pet {
  String get name;
  int get age;
}

@Zorphy()
abstract class $Dog implements $Pet {
  String get breed;
}

final dog = Dog(name: 'Buddy', age: 5, breed: 'Labrador');
final updated = dog.copyWithDog(breed: 'Golden Retriever');
// Updates only Dog-specific fields

4. Patch System

The patch system provides powerful partial updates with nested support:

@Zorphy()
abstract class $User {
  String get name;
  int get age;
}

// Create a patch
final patch = UserPatch.create()
  ..withName('New Name')
  ..withAge(25);

// Apply patch
final updated = user.patchWithUser(patchInput: patch);

Nested Patches

@Zorphy()
abstract class $Address {
  String get street;
  String get city;
}

@Zorphy()
abstract class $User {
  String get name;
  Address get address;
}

// Nested patching
final patch = UserPatch.create()
  ..withName('Updated Name')
  ..withAddressPatch((addrPatch) => addrPatch
    ..withStreet('123 New St')
    ..withCity('New York'));

final updated = user.patchWithUser(patchInput: patch);

Collection Patching

@Zorphy()
abstract class $TodoList {
  String get title;
  List<Todo> get todos;
}

// Update specific item in list
final patch = TodoListPatch.create()
  ..updateTodosAt(0, (todoPatch) => todoPatch
    ..withCompleted(true)
    ..withTitle('Updated Title'));

Functional Patches

// Using functions for computed updates
final patch = CounterPatch.create()
  ..withValue((current) => current + 1);

final updated = counter.patchWithCounter(patchInput: patch);

5. Inheritance & Polymorphism

Multiple Inheritance

@Zorphy()
abstract class $Pet {
  String get name;
  int get age;
}

@Zorphy()
abstract class $Dog {
  String get barkSound;
}

@Zorphy()
abstract class $Cat {
  double get whiskerLength;
}

// Implement multiple interfaces
@Zorphy()
abstract class $FrankensteinsDogCat implements $Dog, $Pet, $Cat {
  // Inherits all fields from all interfaces
}

Generic Inheritance

@Zorphy()
abstract class $$Repository<T> {
  T? find(String id);
  List<T> getAll();
}

@Zorphy()
abstract class $UserRepository implements $$Repository<User> {
  @override
  User? find(String id);

  @override
  List<User> getAll();
}

Generic Classes with Type Constraints

@Zorphy()
abstract class $$Base<T> {
  T get value;
}

@Zorphy()
abstract class $Derived<T extends num> implements $$Base<T> {
  @override
  T get value;

  T get doubled => value * 2;
}

6. Explicit Subtypes & ChangeTo

Use explicitSubTypes to enable cross-type operations:

@Zorphy(explicitSubTypes: [$Dog, $Cat])
abstract class $Pet {
  String get name;
  int get age;
}

@Zorphy()
abstract class $Dog implements $Pet {
  String get breed;
}

@Zorphy()
abstract class $Cat implements $Pet {
  double get whiskerLength;
}

This generates changeTo methods:

final dog = Dog(name: 'Buddy', age: 5, breed: 'Labrador');

// Convert Dog to Cat
final cat = dog.changeToCat(whiskerLength: 3.5);
// Cat(name: 'Buddy', age: 5, whiskerLength: 3.5)

7. CompareTo Feature

Generate comparison methods showing differences between instances:

@Zorphy(generateCompareTo: true)
abstract class $User {
  String get name;
  int get age;
}

final user1 = User(name: 'Alice', age: 30);
final user2 = User(name: 'Alice', age: 35);

final diff = user1.compareToUser(user2);
// {'age': () => 35}

8. Enum Support

Full enum integration with JSON serialization:

enum Status {
  active,
  inactive,
  pending,
}

@Zorphy(generateJson: true)
abstract class $User {
  String get name;
  Status get status;
}

final user = User(name: 'Alice', status: Status.active);
final json = user.toJson();
// {"name": "Alice", "status": "active"}

final fromJson = User.fromJson(json);
// User(name: 'Alice', status: Status.active)

9. Self-Referencing Classes

Handle tree structures and hierarchical data:

@Zorphy(generateJson: true)
abstract class $TreeNode {
  String get value;
  List<TreeNode>? get children;
  TreeNode? get parent;
}

final tree = TreeNode(
  value: 'root',
  children: [
    TreeNode(value: 'child1'),
    TreeNode(value: 'child2'),
  ],
);

10. Factory Methods

Support for custom factory constructors:

@Zorphy()
abstract class $Person {
  String get firstName;
  String get lastName;

  // Custom factory method
  factory $Person.fromNames(String first, String last) = _PersonFromNames;

  // Default factory
  factory $Person.empty() => Person(firstName: '', lastName: '');
}

class _PersonFromNames extends Person {
  _PersonFromNames({
    required String first,
    required String last,
  }) : super(
    firstName: first.toUpperCase(),
    lastName: last.toUpperCase(),
  );
}

// Usage
final person = Person.fromNames('john', 'doe');
// Person(firstName: 'JOHN', lastName: 'DOE')

11. Private Constructors

Use underscore suffix to hide public constructor:

@Zorphy(hidePublicConstructor: true)
abstract class $Config_ {
  String get apiKey;
  String get endpoint;
}

// Define custom factory in the same file
Config createProductionConfig() {
  return Config._(
    apiKey: 'prod-key',
    endpoint: 'https://api.prod.com',
  );
}

Config createDevConfig() {
  return Config._(
    apiKey: 'dev-key',
    endpoint: 'https://api.dev.com',
  );
}

// Usage - Config() constructor is not available
final config = createProductionConfig();

12. Constant Constructors

Enable constant constructors for immutable values:

@Zorphy()
abstract class $Color {
  int get red;
  int get green;
  int get blue;

  const $Color();
}

// Usage
const red = Color(red: 255, green: 0, blue: 0);

13. Nullable Fields

Full null safety support:

@Zorphy()
abstract class $User {
  String get name;
  String? get email;
  String? get phone;
}

final user = User(name: 'Alice');
// email and phone are null

final withEmail = user.copyWith(email: 'alice@example.com');

14. Complex Nested Structures

@Zorphy()
abstract class $Company {
  String get name;
  List<Department> get departments;
}

@Zorphy()
abstract class $Department {
  String get name;
  List<Employee> get employees;
}

@Zorphy()
abstract class $Employee {
  String get name;
  String? get title;
}

// Deep nesting with proper JSON support
final company = Company(
  name: 'Tech Corp',
  departments: [
    Department(
      name: 'Engineering',
      employees: [
        Employee(name: 'Alice', title: 'Engineer'),
      ],
    ),
  ],
);

15. Generic Fields and Methods

typedef JsonConverter<T> = T Function(dynamic);

@Zorphy()
abstract class $ApiResponse<T> {
  bool get success;
  T? get data;
  String? get error;
}

@Zorphy()
abstract class $UserListResponse implements $ApiResponse<List<User>> {
  @override
  bool get success;

  @override
  List<User>? get data;

  @override
  String? get error;
}

16. Combining Multiple Features

enum UserRole {
  admin,
  user,
  guest,
}

@Zorphy(
  generateJson: true,
  generateCopyWithFn: true,
  generateCompareTo: true,
)
abstract class $User {
  String get id;
  String get name;
  String? get email;
  int get age;
  UserRole get role;
  DateTime get createdAt;
  List<String>? get tags;
}

// All features available
final user = User(
  id: '1',
  name: 'Alice',
  age: 30,
  role: UserRole.admin,
  createdAt: DateTime.now(),
);

// CopyWith
final updated = user.copyWith(email: 'alice@example.com');

// Function-based CopyWith
final aged = user.copyWithUserFn(age: () => user.age + 1);

// Patch
final patched = user.patchWithUser(
  patchInput: UserPatch.create()..withTags(['developer', 'dart']),
);

// CompareTo
final diff = user.compareToUser(updated);

// JSON
final json = user.toJson();
final fromJson = User.fromJson(json);

🎨 Annotation Options

The @Zorphy annotation supports these options:

Option Type Default Description
preset ZorphyPreset? null Preset that controls defaults for multiple options (lean, standard, full). When set, per-flag defaults inherit from the preset
generateJson bool false Enable JSON serialization (toJson/fromJson)
generateCopyWith bool? null Generate copyWith methods; inherits from preset if null
generateCopyWithFn bool? null Generate function-based copyWith methods; inherits from preset if null
generatePatch bool? null Generate patch classes for partial updates; inherits from preset if null
generateFilter bool? null Generate filter/query descriptor fields; inherits from preset if null
generateCompareTo bool? null Generate comparison methods; inherits from preset if null
generatePropertyHelpers bool? null Generate property helper extensions; inherits from preset if null
generateEqualsToString bool? null Generate ==, hashCode, and toString; inherits from preset if null
generateChangeTo bool? null Generate changeTo conversion methods between subtypes; inherits from preset if null
explicitSubTypes List<Type> null Specify explicit subtypes for polymorphic operations
explicitToJson bool true Control JSON serialization generation
hidePublicConstructor bool false Hide the public constructor for custom factories
nonSealed bool false Create non-sealed abstract classes instead of sealed

πŸ—οΈ Build Configuration

By default, Zorphy runs as part of the build process. The builder is configured in build.yaml:

targets:
  $default:
    builders:
      zorphy:zorphy:
        enabled: true

Run the generator:

# Build once
dart run build_runner build

# Build and watch for changes
dart run build_runner watch

# Clean generated files
dart run build_runner clean

# Or use the CLI
zorphy build
zorphy build --watch
zorphy build --clean

πŸ“ Naming Conventions

Class Prefixes

  • $ClassName - Concrete class definition (use ClassName in code)
  • $$ClassName - Sealed abstract class (exhaustiveness checking)
  • $ClassName_ - Class with hidden private constructor

Generated Files

  • *.zorphy.dart - Generated code from @Zorphy annotation

Generated Enums

  • ClassName$ - Enum of field names for patch system

Generated Patch Classes

  • ClassNamePatch - Patch class for partial updates

πŸ”„ Migration from Morphy

Zorphy is designed for easy migration from Morphy:

  1. Replace @Morphy with @Zorphy
  2. Replace morphy_annotation dependency with zorphy_annotation
  3. Replace zikzak_morphy dependency with zorphy
  4. Run dart run build_runner clean
  5. Run dart run build_runner build

Key Differences:

  • Better patch system with nested support
  • Enhanced generic handling
  • Improved error messages
  • More flexible inheritance patterns
  • Built-in CLI and MCP server for AI agents

πŸ› Troubleshooting

Type Not Found Errors

If you get type not found errors, ensure:

  1. You've added part 'filename.zorphy.dart'; to your file
  2. You've run dart run build_runner build or zorphy build
  3. The import includes package:zorphy_annotation/zorphy_annotation.dart

Generic Type Issues

When using generics, ensure type parameter names match between parent and child:

// βœ… Correct - Same generic name
abstract class $A<T> { }
abstract class $B<T> implements $A<T> { }

// ❌ Wrong - Different generic names
abstract class $A<T1> { }
abstract class $B<T2> implements $A<T1> { }

Circular Dependencies

Note: @Zorphy2 is deprecated in v2. The single-pass v2 generator handles ordering automatically β€” you no longer need to annotate build-order dependencies manually.

If you are migrating from v1 code that uses @Zorphy2, simply replace all @Zorphy2() annotations with @Zorphy() β€” the v2 generator will resolve the correct build order.

License

MIT License - see LICENSE file for details

Acknowledgments

Inspired by and designed to improve upon the Morphy code generation package.


Made with πŸ”₯ by the ZikZak AI team for the community and AI agents

About

πŸ¦’ Polymorhic Class Generator for Zuraffa. The Dart & Flutter Clean Architecture Framework

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages