copyable 0.0.1+2 copy "copyable: ^0.0.1+2" to clipboard
copyable: ^0.0.1+2 copied to clipboard

A Flutter package for giving and standardizing copy functionality to Dart classes.

copyable #

A package for giving copy capabilities to classes, both local (i.e. in your source code), and foreign (i.e. defined in a third-party library).

Table of Contents

Examples #

The best way to learn is with examples.

Usage #

The copyable package defines two interfaces for giving copy-like functionality to Dart classes: Copyable and Copier.

Once you define a class that implements either of the two interfaces, you can copy instances of the class like this:

Copyable

class Point implements Copyable<Point> {
  final int x;
  final int y;
  final Shape parent;
  
  Point(this.x, this.y, {this.parent});

  // Implement Copyable interface
}

Point origin = Point(0, 0);

// Copy
Point origin_copy = origin.copy();

// Copy, overriding some fields
Point x_intercept = origin.copyWith(x: 5);

Copier

class PointCopier implements Copier<Point> {
  // Implement Copier interface
}

PointCopier pointCopier = PointCopier();
Point origin = Point(0, 0);

// Copy
Point origin_copy = pointCopier.copy(origin);

// Copy, overriding some fields
Point x_intercept = pointCopier.copyWith(x: 5);

Whether to use Copyable or Copier is a matter of preference: the Copyable pattern adds copy functionality directly into a class, where as the Copier pattern adds copy functionality indirectly by creating an entirely new class.

Setup #

Add the following dependency to your pubspec.yaml

dependencies:
  copyable: ^0.0.1

Using the code generation tools requires additional setup.

Copyable #

Interface

/// An interface for copying objects.
abstract class Copyable<T> {
  /// Copy the current object.
  @required T copy();

  /// Copy the current object, overriding with non-null properties of
  /// `master` when present.
  @required T copyWith(T master);

  /// Copy the current object, overriding with the given properties.
  @required T copyWithProperties(/* Add named properties here; typically
  these are object fields.*/);
}

Implementation

import 'package:copyable/copyable.dart';

class Point implements Copyable<Point> {
  final int x;
  final int y;
  Point parent;

  Point({
    this.x,
    this.y,
    this.parent
  });

  // Copyable Implementation
  @override
  Point copy() => _copy(this);

  @override
  Point copyWith(Point master) => _copy(master);

  @override
  Point copyWithProperties({
    int x,
    int y,
    Point parent
  }) => _copy(this,
      x: x,
      y: y,
      parent: parent
  );

  static Point _copy(Point master, {
    int x,
    int y,
    Point parent
  }) {
    return Point(
        x : x ?? master?.x,
        y : y ?? master?.y,
        parent : parent ?? master?.parent
    );
  }
}

Code Generation

If you don't want to manually implement the Copyable interface (I know, it's a lot of boilerplate :/ ), have no fear! Check out the code generation section.

Copier #

Interface

abstract class Copier<T> {
  /// Necessary in order to support chaining of `copy()` calls.
  /// Basically a way to bootstrap a temporary master (from a previous `copy
  /// ()` call), it holds the result of the previous `.copy()` call so that
  /// the user doesn't have to re-wrap the result in a `Copier` instance.
  @required T master;

  /// The default template for copying. This will be the last
  /// fallback for any undefined properties of the object being copied.
  /// Typically just a bare-bones object (i.e. `T()`).
  @required T get defaultMaster;

  // Convenience Methods

  /// Copies the given object `master` and returns a new instance of `Copier<T>`
  /// whose master is the newly copied object.
  @required Copier<T> copy(T master);

  /// Copies the given object `master` and returns it.
  @required T copyAndResolve(T master);

  /// Copies `this.master` with the given parameters only. Returns a new
  /// instance of `Copier<T>` whose master is the newly copied object.
  /// **Note:** Concrete implementations of `Copier<T>` should add optional
  /// named parameters corresponding to the instance properties of `T`.
  @required Copier<T> copyWith(/* {{ Properties here! }} */);

  /// Copies `this.master` with the given parameters only. Returns the newly
  /// copied object.
  /// **Note:** Concrete implementations of `Copier<T>` should add optional
  /// named parameters corresponding to the instance properties of `T`.
  @required T copyWithAndResolve(/* {{ Properties here! }} */);

  /// Returns the result of the most recent copy (i.e. this.master).
  /// Literally the only code in this function should be `return this.master;`
  @required T resolve();
}

Implementation

class CircleCopier implements Copier<Circle> {
  CircleCopier([this.master]);

  Circle master;

  Circle get defaultMaster {
    return Circle(radius: 1);
  }

  dynamic _copy(Circle master,
      {bool resolve = false, int radius, int centerX, int centerY}) {
    master = master ?? this.master;
    Circle newCircle = Circle(
        radius: radius ?? master?.radius ?? defaultMaster.radius,
        centerX: centerX ?? master?.centerX ?? defaultMaster.centerX,
        centerY: centerY ?? master?.centerY ?? defaultMaster.centerY);

    return resolve ? newCircle : CircleCopier(newCircle);
  }

  @override
  CircleCopier copy(Circle master) {
    return this._copy(
      master,
      resolve: false,
    ) as CircleCopier;
  }

  @override
  Circle copyAndResolve(Circle master) {
    return this._copy(
      master,
      resolve: true,
    ) as Circle;
  }

  @override
  CircleCopier copyWith({int radius, int centerX, int centerY}) {
    return this._copy(this.master,
        resolve: false,
        radius: radius,
        centerX: centerX,
        centerY: centerY) as CircleCopier;
  }

  @override
  Circle copyWithAndResolve({int radius, int centerX, int centerY}) {
    return this._copy(this.master,
        resolve: true,
        radius: radius,
        centerX: centerX,
        centerY: centerY) as Circle;
  }

  Circle resolve() {
    return this.master;
  }
}

Code Generation

If you don't want to manually implement the Copier interface (I know, it's a lot of boilerplate :/ ), have no fear! Check out the code generation section.

Code Generation #

Most of the code needed to implement either Copyable or Copier is boilerplate and not fun. Instead, you can generate the respective Copyable or Copier code using build_runner.

Setup #

Add the following dev dependencies to your pubspec.yaml:

dev_dependencies:
  build_runner: ^1.0.0
  build_verify: ^1.1.0

Make sure to run pub get or flutter packages get.

If you don't already a build.yaml file, create one in your project root directory (wherever pubspec.yaml is). Then, add the following to your build.yaml:

targets:
  $default:
    builders:
      # TODO: Add builders
Builders

There are four different builders you can use to generate copy-code. Each builder corresponds to one of the four use cases described earlier in [link here]:

- Copyable Copier
Local copyable copier
Foreign foreignCopyableLib foreignCopierLib

Once you know which builder(s) you want to use, add them your build.yaml with the pattern copyable|{BUILDER_NAME}:

targets:
  $default:
    builders:
      copyable|copyable:
        generate_for:
          - lib/point.dart
      copyable|foreignCopyableLib:
        generate_for:
          - lib/circle.dart
      # More builders if needed...

Usage #

Examples

  1. Import package:copyable/generator.dart.

copyable

Example

  1. Annotate the class you want to generate copy code for with @generate_copyable.
  2. Add the following to the top of your file:
part '$FILE_NAME.g.dart';
  1. Run the builder (see below). This will generate a mixin with the necessary copy-code as a part of file
  2. Add the generated mixin to your original class.

copier

Example

  1. Annotate the class you want to generate copy code for with @GenerateCopier(defaultObjectCode: $DEFAULT).

    In place of $DEFAULT, pass a string of the code to use to instantiate a "default" instance of the class; for example,

    @GenerateCopier(defaultObjectCode: 'Circle(radius: 1')   
    
  2. Add the following to the top of your file:

part '$FILE_NAME.g.dart';
  1. Run the builder (see below). This will generate a separate class with the necessary copy-code as a part of file.

foreignCopyableLib

Example

  1. Create a CopyableMeta instance representing the class you want to generate a copyable version of for.
  2. Create a CopyMetaGenerator instance and pass in the meta object.
  3. Run the builder (see below). This will generate a separate library generated classes.

foreignCopyableLib

Example

  1. Create a CopierMeta instance representing the class you want to generate a copyable version of for.
  2. Create a CopyMetaGenerator instance and pass in the meta object.
  3. Run the builder (see below). This will generate a separate library generated classes.

Running #

In the directory where your build.yaml is, run the following:

Flutter project

flutter packages pub run build_runner build

Non-flutter project

pub run build_runner build
6
likes
40
pub points
37%
popularity

Publisher

unverified uploader

A Flutter package for giving and standardizing copy functionality to Dart classes.

Repository (GitHub)
View/report issues

License

MIT (LICENSE)

Dependencies

analyzer, build, code_builder, flutter, meta, source_gen

More

Packages that depend on copyable