data_class_plugin 0.3.0 data_class_plugin: ^0.3.0 copied to clipboard
A tool that uses Dart's Analyzer to generate code on-the-fly.
Data Class Plugin #
Data Class Plugin is a tool that uses the Dart Analysis Server to generate code on-the-fly.
This package is experimental and still under development, thus do not use it for applications in production.
NOTE: The "in_place" mode has been deprecated and no new features will be added. It's recommended to use it only for prototyping and use "file" mode for production level projects.
Table of contents #
- How it works
- Installation
- File generation
- Generate the code you want
- Configuration
- Notes
- Examples
- Development
- Known Issues
How it works #
Data Class Plugin uses the analyzer system and analyzer plugin to get access on the source code, parse it and provide actions based on that.
Installation #
-
In your project's pubspec.yaml add on
dependencies
the followingdependencies: data_class_plugin: ^0.2.0
-
Update your
analysis_options.yaml
(in case you don't have it, just create a new one)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
Generate the code you want! #
DataClass Annotation #
-
Create a simple class, annotate it with
@DataClass()
and providefinal public
fields for your model.@DataClass() class User { final String id; final String username; }
-
Place the cursor anywhere inside the
User
class -
Run code actions on your IDE
VSCode
- Windows/Linux: Ctrl + .
- MacOS: ⌘ + .
Intellij
- Windows/Linux: Alt + Enter
- MacOS: ⌘ + Enter
-
Select
Generate data class
Available methods are:
-
copyWith
Generates a new instance of the class with optionally provide new fields values.
If no value is provided (default), then true is assumed.
MyClass copyWith(...) { ... }
-
hashAndEquals
Implements hashCode and equals methods.
If no value is provided (default), then true is assumed.
@override bool operator ==(Object other) { ... } @override int get hashCode { ... }
-
$toString
Implements toString method.
If no value is provided (default), then true is assumed.
@override String toString() { ... }
-
fromJson
Generates a factory constructor that creates a new instance from a Map.
If no value is provided (default), then false is assumed.
factory MyClass.fromJson(Map<dynamic, dynamic> json) { ... }
-
toJson
Generates a function that coverts this instance to a Map.
If no value is provided (default), then false is assumed.
Map<String, dynamic> toJson() { ... }
This configuration can be overriden in data_class_plugin_options.yaml
, see Configuration.
Union Annotation #
Adding this annotation to a class enables it to create union types.
Available union annotation toggles are:
-
dataClass
Toggles code generation for toString, copyWith, equals and hashCode.
If no value is provided (default), then true is assumed.
-
toJson
Toggles code generation for fromJson.
If no value is provided (default), then true is assumed.
-
fromJson
Toggles code generation for toJson.
If no value is provided (default), then true is assumed.
Enum Annotation #
-
Create an enumeration with the last field closed by semicolon and annotate it with the
@Enum()
annotation.@Enum() enum Category { science, sports; }
-
Place the cursor anywhere inside the
Category
enum -
Run code actions on your IDE
VSCode
- Windows/Linux: Ctrl + .
- MacOS: ⌘ + .
Intellij
- Windows/Linux: Alt + Enter
- MacOS: ⌘ + Enter
-
Select
Generate enum
Available methods are:
-
$toString
Implements toString method.
If no value is provided (default), then true is assumed.
@override String toString() { ... }
-
fromJson
Generates a factory constructor that creates a new instance from a Map.
If no value is provided (default), then false is assumed.
factory MyClass.fromJson(Map<dynamic, dynamic> json) { ... }
-
toJson
Generates a function that coverts this instance to a Map.
If no value is provided (default), then false is assumed.
Map<String, dynamic> toJson() { ... }
This configuration can be overriden in data_class_plugin_options.yaml
, see Configuration.
Enums #
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
VSCode
- Windows/Linux: Ctrl + .
- MacOS: ⌘ + .
Intellij
- Windows/Linux: Alt + Enter
- MacOS: ⌘ + Enter
-
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 primary 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;
}
Json Converters #
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(cosnt 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.
File generation #
In this mode most of the code generation happens on a generated file.
You still need to generate some boilerplate code on the main class via actions, but most of the code now is generated into a different file (like build_runner).
The generated file has the format of base_filename.gen.dart
To start with this mode you need to:
- Update data_class_plugin_options.yaml (See more options)
generation_mode: file (default in_place)
# This option is **required** if **generation_mode** is "file"
# Specify which path matches should generate files
# If you update this option, you should re-run the generator
# or if it's for a specific folder/file(s) you are working on, you can update this without restarting
file_generation_paths:
- "a/glob/here"
- "an/oth/er/*.dart"
# If you commit the generated files in git
# You can set the line length for the generated code too, so it won't fail in potential CI/CD workflows
generated_file_line_length: 80 (default)
- Add a class
@DataClass()
class User {
String get id;
String get username;
@DefaultValue<String>('')
String get email;
}
Run the code actions like described previously.
The actions will generate for you the constructor, methods and the part directive.
Save the file and run the data_class_plugin CLI to generate
dart run data_class_plugin generate <build | watch>
Configuration #
You can customize the generated code produced by Data Class Plugin.
Configuration file
To create a custom configuration you need to add a file named data_class_plugin_options.yaml
in the root folder of your project.
Available options
-
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. -
enum
Set the default values for the provided methods of the
@Enum
annotation, by specifying the directories where they will be enabled or disabled.
Configuration examples
generation_mode: in_place (default) | file
# This option is **required** if **generation_mode** is "file"
# Which path matches should generate files
# If you update this option, you should re-run the genenator
# or if it's for a specific folder/file(s) you are working on, you can update this without restarting
file_generation_paths:
- "a/glob/here"
- "an/oth/er/*.dart"
# If you commit the generated files in git
# You can set the line length for the generated code too, so it won't fail in potential CI/CD workflows
generated_file_line_length: 80 (default)
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"
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
enabled:
- "a/glob/here"
- "another/glob/here"
disabled:
- "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)
<copy_with | hash_and_equals | to_string | from_json | to_json | unmodifiable_collections>:
default: boolean
enabled:
- "a/glob/here"
- "another/glob/here"
disabled:
- "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
enabled:
- "a/glob/here"
- "another/glob/here"
disabled:
- "a/glob/here"
- "another/glob/here"
Notes #
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 }
Examples #
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.
Development #
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).
Known Issues #
- 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.