Data Class Plugin code generator powered by Tachyon.
- How it works
- Installation
- Generate the code you want
- Configuration
- Notes
- Examples
- Development
- Known Issues
Data Class Plugin uses Tachyon
as it's build engine to provide fast code generation. Also this plugin uses the analyzer system and analyzer plugin
to get access on the source code, parse it and provide code actions
based on that.
These code actions
are similar to the ones provide by the language - e.g. wrap with try/catch
- so you don't need to rely on snippets or manually typing any boilerplate code.
-
In your project's
pubspec.yaml
adddependencies: data_class_plugin: any dev_dependencies: tachyon: any
-
Create
tachyon_config.yaml
on the project's root folderfile_generation_paths: # which files/paths to include for build - "file/path/to/watch" - "another/one" generated_file_line_length: 80 # default line length plugins: - data_class_plugin # register data_class_plugin
-
Update your
analysis_options.yaml
(to enablecode action
)Minimal analysis_options.yaml
include: package:lints/recommended.yaml # You need to register the plugin under analyzer > plugins analyzer: plugins: - data_class_plugin
-
Restart the analysis server
VSCode
- Open the Command Palette
- Windows/Linux: Ctrl + Shift + P
- MacOS: ⌘ + Shift + P
- Type and select "Dart: Restart Analysis Server"
IntelliJ
- Open Find Action
- Windows/Linux: Ctrl + Shift + A
- MacOS: ⌘ + Shift + A
- Type and select "Restart Dart Analysis Server"
- Open the Command Palette
-
Create a simple class, annotate it with
@DataClass()
and provideabstract getter
fields for your model.import 'package:data_class_plugin/data_class_plugin.dart'; @DataClass() class User { String get id; String get username; }
Generated code:
class _$UserImpl extends User { _$UserImpl({ required this.id, required this.username, }) : super.ctor(); @override final String id; @override final String username; @override bool operator ==(Object? other) { return identical(this, other) || other is User && runtimeType == other.runtimeType && id == other.id && username == other.username; } @override int get hashCode { return Object.hashAll(<Object?>[ runtimeType, id, username, ]); } @override String toString() { String toStringOutput = 'User{<optimized out>}'; assert(() { toStringOutput = 'User@<$hexIdentity>{id: $id, username: $username}'; return true; }()); return toStringOutput; } @override Type get runtimeType => User; }
-
Run code actions on your IDE
VSCode
- Windows/Linux: Ctrl + .
- MacOS: ⌘ + .
Intellij
- Windows/Linux: Alt + Enter
- MacOS: ⌘ + Enter
-
Select
Generate data/union classes
This will generate all the boilerplate code on the source file.
Hint: Fields declared with final (e.g. final String id;) will be automatically coverters to abstract getters.
Now to generate the part file code (part directive inserted by the code action) you need to run tachyon.
Executing tachyon:
dart run tachyon build
See more options about tachyon by executing: dart run tachyon --help
Note: As you can see on the screenshot @DataClass
annotations provides a variety of options to choose from. While this is ok, in many cases you might prefer to set these options on path match
or project level
.
Check Configuration section to find more option about data_class_plugin_options.yaml
Adding this annotation to a class enables it to create union types.
This configuration can be overriden in data_class_plugin_options.yaml
, see Configuration.
-
Create an enumeration with the last field closed by semicolon and annotate it with the
@Enum()
annotation.@Enum() enum Category { science, sports; }
-
Select
Generate enum
This configuration can be overriden in data_class_plugin_options.yaml
, see Configuration.
Even if you don't use the @Enum()
annotation, you can still generate methods in enums.
-
Create an enumeration with the last field closed by semicolon
enum Category { science, sports; }
-
Place the cursor anywhere inside the
Category
enum -
Run code actions on your IDE
-
A list with the following actions will be displayed
- Generate constructor
- Generate 'fromJson'
- Generate 'toJson'
- Generate 'toString'
Enums can have an optional single field of primitive type to be used in the fromJson or toJson transforms, if not provided then the
.name
is used as the default json value.
enum Category {
science(0),
sports(1);
final int value;
}
The plugin exposes a json converter registrant that be used through out the app to register your custom converters. This eliminates the need to annotate every single field with a custom converter like (json_serializable).
By default the plugin provides 3 converters for the following classes: Duration, DateTime, Uri.
In case you want to override the default implementation of these converters you can do it by registering your custom converter with jsonConverterRegistrant.register(const MyCustomConverter())
. For more info on how to create and register a converter, see this example
In case you want to provide a custom implementation for a single field that might contain complex logic for parsing/conversion you can use a JsonConverter implementation and annotate the specific field with the implementer class.
See example on ClassWithLatLngConverterAnnotation
class.
If implementing a JsonConverter
is too complex for your case you can use the JsonKey
fromJson/toJson
functions.
You can customize the generated code produced by Data Class Plugin.
To create a custom configuration you need to add a file named data_class_plugin_options.yaml
in the root folder of your project.
-
auto_delete_code_from_annotation
To automatically remove code which can be generated by an action in the source file if the annotation value and the code are in matching.
Based on this example :
@DataClass(toJson: false) class User { Map<String, dynamic> toJson() { // ... } }
When
auto_delete_code_from_annotation
istrue
=>toJson
will be removed because is set asfalse
in the@DataClass
.When
auto_delete_code_from_annotation
isfalse
=>toJson
will be kept as it is even whentoJson
is set asfalse
in the@DataClass
. This allows to create customfromJson/toJson
implementations. -
json
Set the default naming convention for json keys.
You can also override the default naming convention for the specified directories.
Supported naming conventions:
camelCase
,snake_case
,kebab-case
&PascalCase
. -
data_class
Set the default values for the provided methods of the
@DataClass
annotation, by specifying the directories where they will be enabled or disabled. -
union
Set the default values for the provided methods of the
@Union
annotation, by specifying the directories where they will be enabled or disabled. -
enum
Set the default values for the provided methods of the
@Enum
annotation, by specifying the directories where they will be enabled or disabled.
# To automatically remove code generated from an annotation or code that can be generated by an annotation action. (commonly fromJson / toJson)
auto_delete_code_from_annotation: true | false
json:
# Default naming convention for json keys
key_name_convention: camel_case (default) | snake_case | kebab_case | pascal_case
# Maps naming conventions to globs
# You can provide a map of all the conventions you need and then a list with all the globs
# key_name_conventions glob match takes precedence over key_name_convention
key_name_conventions:
<camel_case | snake_case | kebab_case | pascal_case>:
- "a/glob/here"
- "another/glob/here"
# Allows to configure "toJson" code generation
# If no config is provided, null values will be dropped by default
to_json:
options_config:
drop_null_values:
default: boolean # default value is there is no match in enabled or disabled lists
enabled: # list of globs
- "a/glob/here"
- "another/glob/here"
disabled: # list of globs
- "a/glob/here"
- "another/glob/here"
data_class:
options_config:
# For each of the provided methods you can provide a configuration
# The configuration can be an enabled or disabled field that contains a list of globs
# Default values for each options
# copy_with (true), hash_and_equals (true), to_string (true), from_json (false),to_json (false), unmodifiable_collections (true)
<copy_with | hash_and_equals | to_string | from_json | to_json | unmodifiable_collections>:
default: boolean # default value is there is no match in enabled or disabled lists
enabled: # list of globs
- "a/glob/here"
- "another/glob/here"
disabled: # list of globs
- "a/glob/here"
- "another/glob/here"
union:
options_config:
# For each of the provided methods you can provide a configuration
# The configuration can be an enabled or disabled field that contains a list of globs
# Default values for each options
# copy_with (false), hash_and_equals (true), to_string (true) from_json(false), to_json (false), unmodifiable_collections (true), when (true)
<copy_with | hash_and_equals | to_string | from_json | to_json | unmodifiable_collections | when>:
default: boolean # default value is there is no match in enabled or disabled lists
enabled: # list of globs
- "a/glob/here"
- "another/glob/here"
disabled: # list of globs
- "a/glob/here"
- "another/glob/here"
enum:
options_config:
# For each of the provided methods you can provide a configuration
# The configuration can be an enabled or disabled field that contains a list of globs
# Default values for each options
# to_string (false), from_json(false), to_json (false)
<to_string | from_json | to_json>:
default: boolean # default value is there is no match in enabled or disabled lists
enabled: # list of globs
- "a/glob/here"
- "another/glob/here"
disabled: # list of globs
- "a/glob/here"
- "another/glob/here"
If the generated method doesn't exist it will be placed in the end of the class/enum body (before
}
), otherwise it will be re-generated to be up-to-date with current snapshot of the code (fields, annotations configuration).
The constructor is always generated at the start of the body (after
{
) for classes.class MyClass { // constructor will be generated here final int a; }
The constructor is always generated after the semicolon (
;
) in the values declaration for enums.enum MyEnum { a, b, c; // constructor will be generated here }
You can find a variety of examples in the examples folder and the source code from the Live Demo, as it was presented in the Flutter Greek Community, here.
In order to see your changes in the plugin you need to modify tools/analyzer_plugin/pubspec.yaml
and add the following section:
dependency_overrides:
data_class_plugin:
path: /absolute/path/to/root_project
And restart the analysis server (in case that fails run pub_get.sh).
- When using IntelliJ/Android Studio the
$toString
parameter of the @DataClass annotation is not visible in the Suggestions list. However, you can still use it by typing it.