This dart library generates immutable data classes and classes for sum types from
a blueprint mixin. The generated class define structural equality and hashCode
. They
also provide a meaningful implementation of toString
.
Also see this issue.
The library is influence by the data_classes and the sum_types libraries. However, their interfaces are different, so we created a new package.
Add these packages to your dependencies:
dependencies:
sum_data_types: [insert newest version here]
dev_dependencies:
build_runner: ^2.4.4
sum_data_types_generator: [insert newest version here]
A data class is similar to an immutable record. The fields of a data class are used to define
equality and hashCode
in a structural way.
Here is an example:
import 'package:sum_data_types/main.dart';
import 'package:quiver/core.dart';
import 'package:kt_dart/collection.dart';
@DataClass()
mixin User on _UserBase {
String get name;
Optional<int> get age;
KtList<User> get friends;
int numerOfFriends() {
return this.friends.size;
}
}
The @DataClass()
annotation triggers generation of the data class from the mixin
following the annotation.
The mixin should define getters for each property of the data class. The code generation
then generates code for ==
(structural equality), hashCode
and toString
.
Additionally, it generates
a copyWith
method through which you can non-destructively modify some fields of an existing
object of the class (i.e. copyWith
returns a copy of the object).
You can customize the code generation by using the two boolean flags toString
and
eqHashCode
. If you set one of the flags to false
, then no code for toString
or ==
and
hashCode
, respectively, is generated. Example:
@DataClass(toString: false)
mixin UserWithPassword {
String get userId;
String get password;
String toString() {
return "User($userId)"; // do not print the password
}
}
You create instances of the data class by using the static make
method of the
also generated UserFactory
class.
void main() {
final userBob = UserFactory.make(
name: "Bob",
friends: KtList.empty(),
);
final userSarah = UserFactory.make(
name: "Sarah",
friends: KtList.empty(userBob),
age: 31,
);
}
Fields are optional if their type is Optional<...>
, where Optional
comes from
the quiver.core library.
It is recommended to use immutable collections, for example from the
kt.dart package. You can also use the builtin collections
such as List
, but their equality is based on pointer-equality and not on structural equality.
A sum type combines various alternatives of types under a new type. The generated code defines switching functiosn for distinguishing between the different alternatives.
Here is an example:
import 'package:sum_data_types/main.dart';
@SumType()
mixin Either<A, B> on _EitherBase<A, B> {
A get _left;
B get _right;
}
The @SumType()
annotation triggers generation of the data class from the mixin
following the annotation.
The mixin should define getters for each alternative of the sum type. The name
of the getters must start with an underscore. The code generation
then generates code for ==
(structural equality), hashCode
and toString
.
Additionally, it generates
iswitch
and iswitcho
methods which you can use to switch over the various alternatives.
Here is an example use of the iswitch
method:
void foo(Either<String, int> x) {
x.iswitch(
left: (s) => print("String: " + s),
right: (i) => print("int: " + i.toString())
);
}
The iswitcho
method has an additional otherwise
label, so you do not have to cover
all cases when using iswitcho
.
You can customize the code generation by using the two boolean flags toString
and
eqHashCode
. If you set one of the flags to false
, then no code for toString
or ==
and
hashCode
, respectively, is generated.
You create instances of the data class by using the static left
and right
methods of the
also generated EitherFactor
class.
EitherFactory.right(42)