data_classes 3.0.0

  • Readme
  • Changelog
  • Example
  • Installing
  • 84

Hey there! If you're reading this and want data classes to become a language-level feature of Dart, consider giving this issue a thumbs up. 👍

In the meantime, this library generates immutable data classes for you based on simple mutable blueprint classes. Here's how to get started:

1. 📦 Add these packages to your dependencies:

dependencies:
  data_classes: ^3.0.0

dev_dependencies:
  build_runner: ^1.7.1
  data_classes_generator: ^3.0.0

2. 🧬 Write a blueprint class. Let the name start with Mutable and annotate it with @GenerateDataClass():

import 'package:data_classes/data_classes.dart';

part 'my_file.g.dart';

@GenerateDataClass()
class MutableFruit {
  String name;
  @nullable String color;
}

By default, attributes are considered non-nullable. If you want an attribute to be nullable, annotate it with @nullable.

3. 🏭 Run pub run build_runner build in the command line (or flutter pub run build_runner build, if you're using Flutter). The implementation based on your mutable class will automatically be generated.

copy & copyWith

By default, a copy method will be generated that takes a mutating function:

var freshApple = const Fruit(
  name: 'apple',
  color: 'green',
);
var banana = freshApple.copy((fruit) =>
  fruit
    ..name = 'banana'
    ..color = 'yellow'
);

Sometimes, you have classes that are guaranteed to only have non-nullable variables. For those, you can opt in to generate a copyWith method!

@GenerateDataClass(generateCopyWith = true)
class MutableFruit {
  String name;
  String color;
}

var oldApple = freshApple.copyWith(
  color: 'brown',
);

If you're wondering why all fields have to be non-nullable, here's a discussion about that.

value getters #

Sometimes, you have enum values as fields. In that case, you have the option to generate getters directly on the immutable class:

enum Color { red, yellow, green, brown }
enum Shape { round, curved }

@GenerateDataClass()
class MutableFruit {
  @GenerateValueGetters(generateNegations = true)
  Color color;

  @GenerateValueGetters(usePrefix = true)
  Shape theShape;
}

var banana = Fruit(color: Color.yellow, theShape: Shape.curved);

banana.isYellow; // true
banana.isNotGreen; // true
banana.isTheShapeRound; // false

full example #

Here's an example with all the features (except nullability, so copyWith works). To showcase prefixed type imports, the Color enum from above has been moved to a file named colors.dart.

import 'package:data_classes/data_classes.dart';

import 'colors.dart' as colors;

part 'main.g.dart';

enum Shape { round, curved }

/// A fruit with a doc comment.
@GenerateDataClass(generateCopyWith: true)
class MutableFruit {
  String name;

  /// The color of this fruit.
  @GenerateValueGetters(generateNegations: true)
  colors.Color color;

  @GenerateValueGetters(usePrefix: true)
  Shape shape;

  List<String> likedBy;
}

And here's the generated code:


/// A fruit with a doc comment.
@immutable
class Fruit {
  final String name;

  /// The color of this fruit.
  final colors.Color color;

  final Shape shape;

  final List<String> likedBy;

  // Value getters.
  bool get isRed => this.color == colors.Color.red;
  bool get isNotRed => this.color != colors.Color.red;
  bool get isYellow => this.color == colors.Color.yellow;
  bool get isNotYellow => this.color != colors.Color.yellow;
  bool get isGreen => this.color == colors.Color.green;
  bool get isNotGreen => this.color != colors.Color.green;
  bool get isBrown => this.color == colors.Color.brown;
  bool get isNotBrown => this.color != colors.Color.brown;
  bool get isShapeRound => this.shape == Shape.round;
  bool get isShapeCurved => this.shape == Shape.curved;

  /// Default constructor that creates a new [Fruit] with the given
  /// attributes.
  const Fruit({
    @required this.name,
    @required this.color,
    @required this.shape,
    @required this.likedBy,
  })  : assert(name != null),
        assert(color != null),
        assert(shape != null),
        assert(likedBy != null);

  /// Creates a [Fruit] from a [MutableFruit].
  Fruit.fromMutable(MutableFruit mutable)
      : name = mutable.name,
        color = mutable.color,
        shape = mutable.shape,
        likedBy = mutable.likedBy;

  /// Turns this [Fruit] into a [MutableFruit].
  MutableFruit toMutable() {
    return MutableFruit()
      ..name = name
      ..color = color
      ..shape = shape
      ..likedBy = likedBy;
  }

  /// Checks if this [Fruit] is equal to the other one.
  bool operator ==(Object other) {
    return other is Fruit &&
        name == other.name &&
        color == other.color &&
        shape == other.shape &&
        likedBy == other.likedBy;
  }

  int get hashCode {
    return hashList([name, color, shape, likedBy]);
  }

  /// Copies this [Fruit] with some changed attributes.
  Fruit copy(void Function(MutableFruit mutable) changeAttributes) {
    assert(
        changeAttributes != null,
        "You called Fruit.copy, but didn't provide a function for changing "
        "the attributes.\n"
        "If you just want an unchanged copy: You don't need one, just use "
        "the original. The whole point of data classes is that they can't "
        "change anymore, so there's no harm in using the original class.");
    final mutable = this.toMutable();
    changeAttributes(mutable);
    return Fruit.fromMutable(mutable);
  }

  /// Copies this [Fruit] with some changed attributes.
  Fruit copyWith({
    String name,
    colors.Color color,
    Shape shape,
    List<String> likedBy,
  }) {
    return Fruit(
      name: name ?? this.name,
      color: color ?? this.color,
      shape: shape ?? this.shape,
      likedBy: likedBy ?? this.likedBy,
    );
  }

  /// Converts this [Fruit] into a [String].
  String toString() {
    return 'Fruit(\n'
        '  name: $name\n'
        '  color: $color\n'
        '  shape: $shape\n'
        '  likedBy: $likedBy\n'
        ')';
  }
}

[3.0.0] - 2019-10-21

  • Support value getters by annotating the corresponding field with @GenerateValueGetters(). You can optionally set generateNegations and usePrefix to true.
  • Update readme.

[2.1.0] - 2019-10-20

  • Make example not show any errors by ignoring undefined_class and uri_has_nont_been_generated.
  • Replace unused generateCopy field of GenerateDataClassFor class with generateCopyWith.
  • copyWith method now gets generated if you opt-in. Only works if all the fields are non-nullable.
  • Make code more helpful by adding helpful comments at some places and using a StringBuffer instead of returning the output right away.

[2.0.2] - 2019-10-09

  • Support classes with fields that have types which were imported qualified (using import '...' as ...;).
  • Type-promote fields that take generic type arguments.
  • Make freshApple in example const.

[2.0.1] - 2019-09-20

  • Revise readme: Little typo fixes and document build_runner dependency.
  • Code generation now throws error if problems occur.

[2.0.0] - 2019-09-20

  • Change @DataClass() annotation to @GenerateDataClassFor().
  • GeneratedClass.fromMutable() is now a normal constructor instead of a factory constructor.
  • Provide new example.
  • Revise readme.
  • New license.

[1.1.1] - 2019-09-05

  • Fix newline issue in toString().

[1.1.0] - 2019-09-05

  • Change @Nullable() annotation to @nullable.
  • Add toString() method to generated class.
  • Make sure there are no final fields in the mutable class.

[1.0.3] - 2019-09-04

  • Relax those version constraints even further.

[1.0.2] - 2019-09-04

  • Rename example's mutable class to MutableUser.
  • Relax version constraints on analyzer and build_runner in the pubspec.yaml.

[1.0.1] - 2019-09-04

  • Add example.
  • Change blueprint prefix from $ to the more intuitive Mutable.

[1.0.0] - 2019-09-04

  • Initial release: Support DataClass and Nullable annotations. Using the data_classes_generator package, classes with fields, a constructor, converter to and from the original mutable class, custom ==, hashCode and copyWith can get generated.

example/main.dart

// ignore_for_file: undefined_class, uri_has_not_been_generated

import 'package:data_classes/data_classes.dart';

part 'main.g.dart';

void main() {
  const freshApple = const Fruit(type: 'apple', color: 'green');
  var someApple = freshApple.copy((fruit) => fruit..color = null);
  var kiwi = someApple.copy((fruit) => fruit
    ..type = 'Kiwi'
    ..color = 'brown');
  print(kiwi);
}

@GenerateDataClass()
class MutableFruit {
  String type;

  @nullable
  String color;
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  data_classes: ^3.0.0

2. Install it

You can install packages from the command line:

with pub:


$ pub get

with Flutter:


$ flutter pub get

Alternatively, your editor might support pub get or flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:data_classes/data_classes.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
68
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
84
Learn more about scoring.

We analyzed this package on Dec 10, 2019, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.7.0
  • pana: 0.13.1+4

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.2.2 <3.0.0
meta ^1.1.7 1.1.8