cloner 1.0.0 copy "cloner: ^1.0.0" to clipboard
cloner: ^1.0.0 copied to clipboard

Utilities for producing deep clones.

Cloner #

Latest Release pipeline status

BSD 3-Clause License Dart 3.9.2

Deep cloning utilities for Dart collections and custom types.

Features #

  • Element/value-wise deep cloning for List, Set, and Map
  • ICloneable interface for custom types with deep-clone support
  • Collection extensions providing clone() and cloneDynamic() methods
  • MapClone wrapper for typed nested map cloning
  • Pluggable cloner architecture: swap or customize the global cloner
  • Optional circular reference detection (opt-in)
  • Preserves concrete collection types (LinkedHashMap, HashMap, etc.)

Installation #

Add cloner to your pubspec.yaml:

dependencies:
  cloner: ^1.0.0

Then run:

dart pub get

Usage #

Basic Import #

import 'package:cloner/cloner.dart';

Or import only what you need:

import 'package:cloner/core.dart';       // Cloner class + interfaces
import 'package:cloner/extensions.dart'; // Collection extensions + MapClone

Implementing ICloneable #

Define custom types that support deep cloning:

class Address implements ICloneable<Address> {
  String street;
  int number;

  Address(this.street, this.number);

  @override
  Address clone() => Address(street, number);
}

class Person implements ICloneable<Person> {
  String name;
  Address address;

  Person(this.name, this.address);

  @override
  Person clone() => Person(name, address.clone());
}

Cloning with Cloner #

The Cloner facade provides a pluggable, global cloner reference and supports custom options.

Basic deep cloning

final list = [1, 2, 3];
final cloned = Cloner.instance(doTypedClone: true).cloneList(list);
cloned[0] = 99;
print(list);   // [1, 2, 3]
print(cloned); // [99, 2, 3]

Swapping the global cloner

You can replace the global cloner for all subsequent operations:

Cloner.reference = MyCustomCloner();

Collection Extensions #

Use clone() for typed cloning or cloneDynamic() for untyped cloning. Both respect the current Cloner.reference and accept an optional doCircRefCheck parameter.

// List
final numbers = [1, 2, 3];
final clonedNumbers = numbers.clone(); // List<int>

// Set
final tags = {'a', 'b', 'c'};
final clonedTags = tags.clone(); // Set<String>

// Map
final scores = {'alice': 100, 'bob': 95};
final clonedScores = scores.clone(); // Map<String, int>

// Dynamic clone for heterogeneous collections
final mixed = [1, 'two', {'key': 'value'}];
final clonedMixed = mixed.cloneDynamic(); // List<dynamic>

// Enable circular reference detection (optional)
final circSafe = numbers.clone(doCircRefCheck: true);

MapClone for Nested Typed Maps #

Use MapClone when you need typed deep cloning of nested maps:

final config = MapClone<String, MapClone<String, int>>.ofMap({
  'limits': MapClone.ofMap({'maxRetries': 3, 'timeout': 30}),
});

final cloned = config.clone();
cloned['limits']!['maxRetries'] = 10;

print(config['limits']!['maxRetries']); // 3 (unchanged)
print(cloned['limits']!['maxRetries']); // 10

MapClone also provides copy() for shallow copies:

final original = MapClone<String, List<int>>();
original['data'] = [1, 2, 3];

final shallow = original.copy();  // Lists are shared
final deep = original.clone();    // Lists are cloned

Circular Reference Detection #

Note

Enabling circular reference detection incurs a performance cost and is recommended only for debugging or when cycles are possible.

By default, circular reference detection is off. To enable it (and throw CircularReferenceCloneException on cycles):

final cyclic = [];
cyclic.add(cyclic);

try {
  final clone = Cloner.instance(doCircRefCheck: true).cloneValue(cyclic);
} on CircularReferenceCloneException catch (exc) {
  print('Cycle detected: $exc');
}

[!TIP] You can also enable it via collection extensions:

final clone = myList.clone(doCircRefCheck: true);

Typed vs Dynamic Cloning #

  • Typed cloning (clone()) preserves generic types but throws UnsupportedTypedCloneException if a nested plain Map is encountered. Wrap nested maps with MapClone for typed cloning.

  • Dynamic cloning (cloneDynamic()) returns dynamic element/value types and handles any nested structure including plain maps.

License #

BSD 3-Clause License
This work is licensed under the BSD 3-Clause License.

0
likes
0
points
96
downloads

Publisher

unverified uploader

Weekly Downloads

Utilities for producing deep clones.

Repository (GitLab)
View/report issues

License

unknown (license)

More

Packages that depend on cloner