$npx -y skills add flutter/agent-plugins --skill dart-use-primary-constructorsHelp users write syntactically and semantically correct primary constructors in Dart, and migrate/use the new constructor syntax, empty-body semicolon syntax, in-body initializer list syntax, and abbreviated concise constructor syntax.
| 1 | # Dart Primary Constructors & New Constructor Syntax Skill |
| 2 | |
| 3 | Use this skill when helping users write, refactor, or debug code using Dart's **Primary Constructors** feature. |
| 4 | |
| 5 | ### Dart Version Requirements |
| 6 | * **Dart 3.13 and above**: Primary constructors are enabled by default. |
| 7 | * **Dart 3.12**: The feature is available but experimental. Users must explicitly enable the experiment flag `primary-constructors` via `--enable-experiment=primary-constructors` or in `analysis_options.yaml`: |
| 8 | ```yaml |
| 9 | analyzer: |
| 10 | enable-experiment: |
| 11 | - primary-constructors |
| 12 | ``` |
| 13 | * **Dart 3.11 and earlier**: Primary constructors are not supported. |
| 14 | |
| 15 | --- |
| 16 | |
| 17 | ## 1. Overview |
| 18 | Primary Constructors allow developers to declare a non-redirecting generative constructor as well as a set of instance variables directly in the class header. This significantly reduces boilerplate and improves code readability. |
| 19 | |
| 20 | ### Key Benefits |
| 21 | - Combines field declaration, parameter declaration, and initialization into a single declaration known as a declaring parameter declaration. |
| 22 | - Enables safe reference to constructor parameters in non-late field initializers (Primary Initializer Scope). |
| 23 | - Allows empty declaration bodies to be represented concisely with a semicolon (`;`). |
| 24 | - Introduces abbreviated concise syntax for in-body constructors. |
| 25 | |
| 26 | --- |
| 27 | |
| 28 | ## 2. Syntax Reference |
| 29 | |
| 30 | ### 2.1 Basic Class Header Syntax |
| 31 | To declare a primary constructor, place a parameter list immediately after the type name (and optional type parameters): |
| 32 | |
| 33 | ```dart |
| 34 | // Declares fields x and y, and a generative constructor Point(this.x, this.y) |
| 35 | class Point(var int x, var int y); |
| 36 | |
| 37 | // Declares final fields |
| 38 | class PointFinal(final int x, final int y); |
| 39 | ``` |
| 40 | |
| 41 | ### 2.2 Declaring, Initializing, and Plain Parameters |
| 42 | A primary constructor parameter list distinguishes between three types of parameters: |
| 43 | 1. **Declaring Parameters**: Indicated by the `var` or `final` modifier (e.g., `final int x`). They implicitly create a corresponding instance field in the class. |
| 44 | 2. **Initializing Parameters**: Indicated by the `this.` or `super.` prefix (e.g., `this.x` or `super.x`). They initialize an existing field or a super constructor parameter, respectively. |
| 45 | 3. **Regular Parameters**: Declared without modifiers (e.g., `int y`). They do not become fields and are only available during initialization (e.g., in field initializers or the `this :` initializer list in the class body). |
| 46 | |
| 47 | ```dart |
| 48 | // `x` is a field and a parameter because it has the keyword `final`. In particular, we can use the name `x` in the initializer list in the in-body part of the primary constructor. 'y' is a only parameter because it has neither of the keywords `final` or `var`, but `y` is passed to the super constructor via the `this :` initializer list. |
| 49 | class C(final int x, int y) extends Base { |
| 50 | this : super(y); |
| 51 | } |
| 52 | ``` |
| 53 | |
| 54 | Declaring parameters and initializing parameters are two ways of achieving the same goal: declaring a class with instance fields which are set in the constructor. Regular parameters are different in that their values are not automatically routed to an instance field. |
| 55 | |
| 56 | ### 2.3 Constant Primary Constructors |
| 57 | To make a primary constructor `const`, place the `const` keyword before the class/type name in the declaration header: |
| 58 | |
| 59 | ```dart |
| 60 | class const Point(final int x, final int y); |
| 61 | extension type const Ext(int x); |
| 62 | enum const MyEnum(final int x) { |
| 63 | entry(1); |
| 64 | } |
| 65 | ``` |
| 66 | |
| 67 | ### 2.4 Extension Types |
| 68 | Extension types **must** use primary constructors. |
| 69 | - The single parameter in the header is the representation field. |
| 70 | - The representation variable cannot use the `var` modifier (using `var` triggers the `representation_field_modifier` error). |
| 71 | - The representation variable can optionally use the `final` modifier. If `final` is not present then it is inferred; that is, the parameter is declaring whether or not it's explicitly `final`. |
| 72 | |
| 73 | ### 2.5 Empty Body Semicolon Shorthand (`;`) |
| 74 | When a class, mixin class, mixin, extension or extension type has an empty body, the `{}` braces can be replaced by a semicolon (`;`): |
| 75 | |
| 76 | ```dart |
| 77 | class C(int x); |
| 78 | mixin class MC; |
| 79 | extension type ET(int x); |
| 80 | mixin M; |
| 81 | extension Ext on C; |
| 82 | ``` |
| 83 | |
| 84 | ### 2.6 The In-Body Part of a Primary Constructor (`this ...`) |
| 85 | If a primary constructor requires assertions or custom field initializations, they can be declared in the body using the `this :` syntax: |
| 86 | |
| 87 | ```dart |
| 88 | class Point(var int x, var int y) { |
| 89 | // Initializer list in class body |
| 90 | this : assert(x >= 0), y = y * 2; |
| 91 | } |
| 92 | ``` |
| 93 | |
| 94 | You can also write a constructor body with this syntax (`this {...}`). |
| 95 | |
| 96 | ### 2.7 Abbreviated Concise Constructor Syntax |
| 97 | For constructors declared within t |