im_model_gen 1.2.0 copy "im_model_gen: ^1.2.0" to clipboard
im_model_gen: ^1.2.0 copied to clipboard

Annotation builder for immutable model to generate of `copyWith` and == (equal) operator..

This library provides code generation for immutable models with value comparison == and copyWith methods.

Project targets:

  • Standard syntax.
  • errors before runtime.
  • Low source code generation, your IDE will be happy.
  • Improved output binary size.
  • Inheritance unlocked (while encouraging composition every day, "is-a" pattern is not bad).
  • to be not too intrusive.

Requirements #

Requirement dart SDK >= 3.0.0

Usage #

Annotate your class with ImModel annotation

import 'package:im_model/im_model.dart';

part 'example.g.dart'; // Declare generated part file

@ImModel()
class Example with _$ExampleMixin { // Associate your class with the generated mixin
  final String id;
  final String? name;
  final ImList<int> values; // ImList is part of this package.

  const Example(this.id, {this.name, this.values = const ImList.empty()});
}
const example = Example(id: 'id');

// Copy
example.copyWith(text: 'test'); // Example(id: "id", text: test, values; [])
example.copyWith(text: null); // Example(id: "id", text: null, values: [])

// Equality
const exampleTwo = Example(id: 'id');
example == exampleTwo; // true
example == exampleTwo.copyWith(values: [1]); // false

// Immutability
// example.values.add(1); // 'add' is undefined.

Code generation command

dart run build_runner build --delete-conflicting-outputs

More on features #

There are two annotations available.

ImModel on classes with those parameters:

  • ignoreCopy: allows to ignore the copy generation. Defaults to false.
  • ignoreEqual: allows to ignore the equality generation. Defaults to false.
  • copyConstructor: If needed, you can setup a named constructor for copy generation. Defaults to null.

ImModel on class fields to superseed class annotation with those parameters:

  • ignoreCopy: allows to ignore the copy generation. Defaults to null.
  • ignoreEqual: allows to ignore the equality generation. Defaults to null.

Collections #

This package provides immutable collections by simply wrapping the core ones.

You must prefix all your mutable collections to their immutable counterpart.

  • List => ImList
  • Map => ImMap
  • Set => ImSet

The code generator will provide error messages if it detects mutable collections.

To facilitate mutation on collections there are two getters:

  • mut => to get a mutable version of the collection. You can use it at no cost, the collection is copied only if you modify it.
  • immut => to get the immutable version of the collection.

Look at the example below for demonstration.

⚠️

  • Obviously, like anywhere else, the collections are immutable only if the elements are immutable.

  • At this time, if you use a custom collection implementation, the package won't be able to detect it. So things like MyFooList<int> or ImList<MyFooList<int>> will be allowed, but ImList<Set<int>> or other variants won't.

Equality #

This package uses Dart 3 records to make ImModels comparable.

Also, an ImModel enforces equality on models of the same type.

Now, things like MyImModel() == Object() will trigger an error at compile time.

Finally, the performance is completely determined by Dart internal libraries, partially on Im* collections.

(More) advanced usage #

Now that we have all discovered, here's a full (fancy) example:

/// Inverted ignore flags on class => superseeded by fields
/// [id] is not part of `copyWith`.
/// Only [id] is part of equality.
@ImModel(ignoreEqual: true, ignoreCopy: true)
class Parent<T> with _$ParentMixin<T> {
  @ImField(ignoreEqual: false)
  final String id;
  @ImField(ignoreCopy: false)
  final T? aValue;

  const Parent(this.id, this.aValue);
}

/// [collection] class member is immutable (you can't use add, remove, ...).
/// [id] is not part of `copyWith`.
/// Only [id] and [collection] are part of equality.
@ImModel()
class Child<T> extends Parent<T> with _$ChildMixin<T> {
  final ImList<int> collection;

  const Child(super.id, super.aValue, {required this.collection});
}
void main() {
  var obj1 = Child('a', 0, collection: [1].immut);
  var obj2 = Child('a', 0, collection: ImList([1]));
  // obj1 == obj2 => true

  obj1 = obj1.copyWith(collection: obj1.collection.mut..add(2));
  // obj1 == obj2 => false

  // Two things to notice here:
  // - we used `mut` getter to mutate the initial collection for shorter syntax.
  // - we didn't had to wrap again the collection to be immutable, this is done in generated code.

  // obj1.copyWith(id: 'b');
  // The named parameter 'id' isn't defined.
}

Improved output size #

Todo.

For now, I'll give you my personal experience.

I developed this package for professional needs and the resulting output size from a 8.2MB web app has decreased by 650KB => 8% (main.dart.js considered only).

What if I do love Freezed? #

This project diverges by targeting output size of your app and doesn't use "specific" syntax for the same result, improving our coding performance.

Freezed does not guarantee in any way that your collections are immutable when coding.

I consider this package not too intrusive to allow you to get away easily if you(we) find a more suitable or definitive solution.

Final word #

Made with two fingers and a keyboard late at night.

1
likes
130
points
98
downloads

Publisher

verified publisheropenapi4j.org

Weekly Downloads

Annotation builder for immutable model to generate of `copyWith` and == (equal) operator..

Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

analyzer, build, im_model, meta, source_gen

More

Packages that depend on im_model_gen