+
Skip to content
/ morphy Public
forked from atreeon/morphy

Provides a clean class definition with extra functionality including; copy with, json serializable, tostring, equals that supports inheritance and polymorphism

Notifications You must be signed in to change notification settings

arrrrny/morphy

 
 

Repository files navigation

ZikZak Morphy

A powerful code generation package for Dart/Flutter that provides clean class definitions with advanced features including copyWith methods, JSON serialization, toString, equality, and comprehensive inheritance support.

Package Structure

This repository contains two packages:

  • zikzak_morphy/ - Main code generation package
  • zikzak_morphy_annotation/ - Annotations package

Installation

Add these dependencies to your pubspec.yaml:

dependencies:
  zikzak_morphy_annotation: ^2.0.0

dev_dependencies:
  zikzak_morphy: ^2.0.0
  build_runner: ^2.5.4

Features

  • Type-safe copyWith methods with full inheritance support
  • JSON serialization/deserialization with automatic handling
  • Automatic toString and equality implementations
  • Patch-based updates for complex object modifications
  • Nested patch operations for deep updates in object hierarchies
  • Collection patching for Lists and Maps containing Morphy objects
  • changeTo methods for type transformations between related classes
  • Multiple inheritance support for interfaces
  • Generic type handling
  • Private constructor support
  • Clean architecture patterns

Quick Start

  1. Annotate your classes with @Morphy():
import 'package:zikzak_morphy_annotation/morphy_annotation.dart';

part 'user.morphy.dart';

@Morphy()
abstract class $User {
  String get name;
  int get age;
  String? get email;
  
  // Factory methods for clean object creation
  factory $User.create(String name, int age) =>
      User._(name: name, age: age, email: null);

  factory $User.withEmail({
    required String name,
    required int age,
    required String email,
  }) => User._(name: name, age: age, email: email);
}
  1. Run code generation:
dart run build_runner build
  1. Use the generated features:
// Create instances using factory methods
final user = User.create('John', 30);
final emailUser = User.withEmail(
  name: 'Jane', 
  age: 25, 
  email: 'jane@example.com'
);

// Type-safe copyWith
final updatedUser = user.copyWithUser(age: () => 31);

// Patch-based updates for complex modifications
final userPatch = UserPatch.create()
  ..withName('John Updated')
  ..withAge(31);
final patchedUser = userPatch.applyTo(user);

// JSON serialization (when generateJson: true)
final json = user.toJson();
final fromJson = User.fromJson(json);

// Automatic toString and equality
print(user); // (User-name:John|age:30|email:null)
print(user == updatedUser); // false

Nested Patch Operations

For complex object hierarchies, Morphy now supports deep patching of nested objects and collections:

@Morphy()
abstract class $Profile {
  String get name;
  int get age;
}

@Morphy()
abstract class $User {
  String get email;
  $Profile get profile;
  List<$Address> get addresses;
  Map<String, $Contact> get contacts;
}

// Deep nested patching with function-based approach
final userPatch = UserPatch.create()
  ..withEmail('new@example.com')
  ..withProfilePatchFunc((patch) => patch
    ..withName('Updated Name')
    ..withAge(35))
  ..updateAddressesAt(0, (patch) => patch
    ..withStreet('New Street'))
  ..updateContactsValue('work', (patch) => patch
    ..withPhone('555-0123'));

final updatedUser = userPatch.applyTo(originalUser);

Patch Methods Generated

For nested Morphy objects, the following methods are automatically generated:

  • withFieldPatch(FieldPatch value) - Direct patch application
  • withFieldPatchFunc(FieldPatch Function(FieldPatch) updater) - Function-based patching
  • updateListFieldAt(int index, ItemPatch Function(ItemPatch) updater) - List item patching
  • updateMapFieldValue(KeyType key, ValuePatch Function(ValuePatch) updater) - Map value patching

Advanced Features

Factory Constructors

Define named constructors with custom logic directly in your abstract class:

@Morphy(hidePublicConstructor: true)
abstract class $Product {
  String get id;
  String get name;
  double get price;
  String get category;
  bool get isActive;

  // Factory with validation logic
  factory $Product.discounted({
    required String name,
    required double originalPrice,
    required double discountPercent,
  }) {
    final discountedPrice = originalPrice * (1 - discountPercent / 100);
    return Product._(
      id: 'DISC_${DateTime.now().millisecondsSinceEpoch}',
      name: '$name (${discountPercent.toInt()}% OFF)',
      price: discountedPrice,
      category: 'sale',
      isActive: true,
    );
  }

  // Factory from external data
  factory $Product.fromApi(Map<String, dynamic> apiData) {
    return Product._(
      id: apiData['product_id'] ?? '',
      name: apiData['display_name'] ?? 'Unknown',
      price: (apiData['price_cents'] ?? 0) / 100.0,
      category: apiData['category'] ?? 'general',
      isActive: apiData['status'] == 'active',
    );
  }
}

// Usage
var saleProduct = Product.discounted(
  name: "Premium Widget",
  originalPrice: 100.0,
  discountPercent: 25.0,
);

var apiProduct = Product.fromApi({
  'product_id': 'PROD123',
  'display_name': 'API Widget',
  'price_cents': 2500,
  'status': 'active',
});

Self-Referencing Classes

Handle complex tree structures and hierarchical data:

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

  // Factory methods for different node types
  factory $TreeNode.root(String value) =>
      TreeNode._(value: value, children: [], parent: null);

  factory $TreeNode.leaf(String value, TreeNode parent) =>
      TreeNode._(value: value, children: null, parent: parent);

  factory $TreeNode.branch(
    String value,
    TreeNode parent,
    List<TreeNode> children,
  ) => TreeNode._(value: value, children: children, parent: parent);
}

// Build a file system tree
var rootDir = TreeNode.root("/");
var homeDir = TreeNode.leaf("home", rootDir);
var userDir = TreeNode.leaf("john", homeDir);

// Navigate and modify
var updatedRoot = rootDir.copyWithTreeNode(
  children: () => [homeDir, TreeNode.leaf("tmp", rootDir)]
);

Type Transformations

Transform between related classes in inheritance hierarchies:

@Morphy(generateJson: true)
abstract class $Person {
  String get name;
  int get age;
  String? get department;
}

@Morphy(generateJson: true)
abstract class $Manager extends $Person {
  int get teamSize;
  List<String> get responsibilities;
  double get salary;
  String get role;
}

// Transform Person to Manager (demonstrates our changeTo fix)
final person = Person.basic('Alice', 35, 'Engineering');
final manager = person.changeToManager(
  teamSize: 10,
  responsibilities: ['Planning', 'Development'],
  salary: 120000.0,
  role: 'Tech Lead',
);

Key Improvements

This enhanced version fixes critical issues in the original Morphy package:

  • Fixed changeTo method generation - No longer attempts to access non-existent fields on source classes
  • Enhanced type safety - Proper handling of inheritance hierarchies
  • Improved patch system - Better null safety and error handling
  • Clean architecture support - Designed for modern Flutter development patterns

Documentation

For detailed documentation, examples, and API reference, see the individual package README files:

Testing

The factory_test/ directory contains comprehensive tests demonstrating all package features and ensuring code generation works correctly.

Credits

This package is based on the excellent work from the original Morphy package by @atreon. We extend our gratitude for the foundational architecture and innovative approach to Dart code generation.

Contributing

Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Provides a clean class definition with extra functionality including; copy with, json serializable, tostring, equals that supports inheritance and polymorphism

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Dart 95.5%
  • Shell 4.5%
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载