Effective Dart Skill
This skill defines how to write idiomatic, high-quality Dart and Flutter code following Effective Dart guidelines.
1. Naming Conventions
| Kind |
Convention |
Example |
| Classes, enums, typedefs, type parameters, extensions |
UpperCamelCase |
MyWidget, UserState |
| Packages, directories, source files |
lowercase_with_underscores |
user_profile.dart |
| Import prefixes |
lowercase_with_underscores |
import '...' as my_prefix; |
| Variables, parameters, named parameters, functions |
lowerCamelCase |
userName, fetchData() |
- Capitalize acronyms and abbreviations longer than two letters like words:
HttpRequest, not HTTPRequest.
- Avoid abbreviations unless the abbreviation is more common than the full term.
- Prefer putting the most descriptive noun last in names.
- Use terms consistently throughout your code.
- Follow mnemonic conventions for type parameters:
E (element), K/V (key/value), T/S/U (generic types).
- Consider making code read like a sentence when designing APIs.
- Prefer a noun phrase for non-boolean properties or variables.
- Prefer a non-imperative verb phrase for boolean properties or variables; prefer the positive form.
- Consider omitting the verb for named boolean parameters.
- Avoid starting a function or method name with
get; prefer removing get and using a getter when the API conceptually exposes a property.
2. Types and Functions
- Use class modifiers (
final, sealed, interface, base, mixin) to control whether a class can be extended or implemented.
- Type annotate variables without initializers.
- Type annotate fields and top-level variables if the type isn't obvious.
- Annotate return types on function declarations.
- Annotate parameter types on function declarations.
- Write type arguments on generic invocations that aren't inferred.
- Annotate with
dynamic instead of letting inference fail.
- Use
Future<void> as the return type of async members that do not produce values.
- Use getters for operations that conceptually access properties.
- Use setters for operations that conceptually change properties.
- Use a function declaration to bind a function to a name.
- Use inclusive start and exclusive end parameters to accept a range.
// Prefer: explicit class modifier
final class AppConfig {
final String apiUrl;
final int timeout;
const AppConfig({required this.apiUrl, required this.timeout});
}
// Prefer: sealed for exhaustive pattern matching
sealed class Result<T> {}
class Success<T> extends Result<T> { final T value; Success(this.value); }
class Failure<T> extends Result<T> { final Exception error; Failure(this.error); }
3. Style
dart format .
- Format code with
dart format — don't manually format.
- Use curly braces for all flow control statements.
- Prefer
final over var when variable values won't change.
- Use
const for compile-time constants.
- Prefer lines 80 characters or fewer for readability.
4. Imports and Files
- Don't import libraries inside the
src directory of another package.
- Don't allow import paths to reach into or out of
lib.
- Prefer relative import paths within a package.
- Don't use
/lib/ or ../ in import paths.
- Consider writing a library-level doc comment for library files.
5. Structure
- Keep files focused on a single responsibility.
- Limit file length to maintain readability.
- Group related functionality together.
- Prefer making fields and top-level variables
final.
- Consider making constructors
const if the class supports it.
- Prefer making declarations private — only expose what's necessary.
6. Usage Patterns
// Adjacent string concatenation (not +)
final greeting = 'Hello, '
'world!';
// Collection literals
final list = [1, 2, 3];
final map = {'key': 'value'};
// Initializing formals
class Point {
final double x, y;
Point(this.x, this.y);
}
// Empty constructor body
class Empty {
Empty(); // not Empty() {}
}
// rethrow to preserve stack trace
try {
doSomething();
} catch (e) {
log(e);
rethrow;
}
- Use
whereType<T>() to filter a collection by type.
- Follow a consistent rule for
var and final on local variables.
- Initialize fields at their declaration when possible.
- Override
hashCode if you override ==; ensure == obeys mathematical equality rules.
- Prefer specific exception handling: use
on SomeException catch (e) instead of broad catch (e) or .catchError handlers.
7. Documentation
/// Returns the sum of [a] and [b].
///
/// Throws [ArgumentError] if either value is negative.
int add(int a, int b) { ... }
- Format comments like sentences (capitalize, end with period).
- Use
/// doc comments — not /* */ block comments — for types and members.
- Prefer writing doc comments for public APIs; consider them for private APIs too.
- Start doc comments with a single-sentence summary, separated into its own paragraph.
- Avoid redundancy with the surrounding context.
- Start function/method comments with a third-person verb if the main purpose is a side effect.
- Start with a noun or non-imperative verb phrase if returning a value is the primary purpose.
- Start boolean variable/property comments with "Whether" followed by a noun or gerund phrase.
- Use
[identifier] in doc comments to refer to in-scope identifiers.
- Use prose to explain parameters, return values, and exceptions (e.g., "The [param]", "Returns", "Throws" sections).
- Put doc comments before metadata annotations.
- Document why code exists or how it should be used, not just what it does.
8. Testing Patterns
- Write unit tests for business logic, using
group and descriptive test names:
import 'package:test/test.dart';
void main() {
group('CartService', () {
late CartService cart;
setUp(() => cart = CartService());
test('addItem increases item count', () {
cart.addItem(Product(id: '1', name: 'Widget', price: 9.99));
expect(cart.items, hasLength(1));
});
test('removeItem decreases total price', () {
final product = Product(id: '1', name: 'Widget', price: 9.99);
cart.addItem(product);
cart.removeItem(product.id);
expect(cart.totalPrice, equals(0.0));
});
});
}
- Write widget tests using
testWidgets and WidgetTester:
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('LoginButton shows loading indicator when tapped',
(WidgetTester tester) async {
await tester.pumpWidget(const MaterialApp(home: LoginScreen()));
await tester.tap(find.byType(ElevatedButton));
await tester.pump();
expect(find.byType(CircularProgressIndicator), findsOneWidget);
});
}
9. Code Review Workflow
When reviewing Dart code for Effective Dart compliance, the agent should check:
- Naming — verify all identifiers follow the conventions in Section 1.
- Type annotations — confirm public API parameters, return types, and uninitialized variables are annotated.
- Class modifiers — verify
final, sealed, or interface is used where appropriate.
- Documentation — confirm all public members have
/// doc comments with a single-sentence summary.
- Style — run
dart format --output=none --set-exit-if-changed . to verify formatting.
- Analysis — run
dart analyze and confirm zero issues.
References
1---2name: effective-dart3description: Use when writing Dart code, reviewing for style, refactoring naming, adding doc comments, structuring imports, or enforcing type annotations.4---5
6# Effective Dart Skill
7
8This skill defines how to write idiomatic, high-quality Dart and Flutter code following Effective Dart guidelines.
9
10---
11
12## 1. Naming Conventions
13
14| Kind | Convention | Example |
15|---|---|---|
16| Classes, enums, typedefs, type parameters, extensions | `UpperCamelCase` | `MyWidget`, `UserState` |
17| Packages, directories, source files | `lowercase_with_underscores` | `user_profile.dart` |
18| Import prefixes | `lowercase_with_underscores` | `import '...' as my_prefix;` |
19| Variables, parameters, named parameters, functions | `lowerCamelCase` | `userName`, `fetchData()` |
20
21- Capitalize acronyms and abbreviations longer than two letters like words: `HttpRequest`, not `HTTPRequest`.
22- Avoid abbreviations unless the abbreviation is more common than the full term.
23- Prefer putting the **most descriptive noun last** in names.
24- Use terms **consistently** throughout your code.
25- Follow mnemonic conventions for type parameters: `E` (element), `K`/`V` (key/value), `T`/`S`/`U` (generic types).
26- Consider making code **read like a sentence** when designing APIs.
27- Prefer a **noun phrase** for non-boolean properties or variables.
28- Prefer a **non-imperative verb phrase** for boolean properties or variables; prefer the positive form.
29- Consider omitting the verb for named boolean parameters.
30- Avoid starting a function or method name with `get`; prefer removing `get` and using a getter when the API conceptually exposes a property.
31
32---
33
34## 2. Types and Functions
35
36- Use **class modifiers** (`final`, `sealed`, `interface`, `base`, `mixin`) to control whether a class can be extended or implemented.
37- **Type annotate variables** without initializers.
38- Type annotate **fields and top-level variables** if the type isn't obvious.
39- **Annotate return types** on function declarations.
40- **Annotate parameter types** on function declarations.
41- Write **type arguments** on generic invocations that aren't inferred.
42- Annotate with `dynamic` instead of letting inference fail.
43- Use `Future<void>` as the return type of async members that do not produce values.
44- Use **getters** for operations that conceptually access properties.
45- Use **setters** for operations that conceptually change properties.
46- Use a **function declaration** to bind a function to a name.
47- Use **inclusive start and exclusive end** parameters to accept a range.
48
49```dart
50// Prefer: explicit class modifier
51final class AppConfig {
52 final String apiUrl;
53 final int timeout;
54 const AppConfig({required this.apiUrl, required this.timeout});
55}
56
57// Prefer: sealed for exhaustive pattern matching
58sealed class Result<T> {}
59class Success<T> extends Result<T> { final T value; Success(this.value); }
60class Failure<T> extends Result<T> { final Exception error; Failure(this.error); }
61```
62
63---
64
65## 3. Style
66
67```bash
68dart format .
69```
70
71- Format code with `dart format` — don't manually format.
72- Use **curly braces** for all flow control statements.
73- Prefer `final` over `var` when variable values won't change.
74- Use `const` for compile-time constants.
75- Prefer lines **80 characters or fewer** for readability.
76
77---
78
79## 4. Imports and Files
80
81- Don't import libraries inside the `src` directory of another package.
82- Don't allow import paths to reach into or out of `lib`.
83- **Prefer relative import paths** within a package.
84- Don't use `/lib/` or `../` in import paths.
85- Consider writing a **library-level doc comment** for library files.
86
87---
88
89## 5. Structure
90
91- Keep files **focused on a single responsibility**.
92- Limit file length to maintain readability.
93- Group related functionality together.
94- Prefer making fields and top-level variables `final`.
95- Consider making constructors `const` if the class supports it.
96- **Prefer making declarations private** — only expose what's necessary.
97
98---
99
100## 6. Usage Patterns
101
102```dart
103// Adjacent string concatenation (not +)
104final greeting = 'Hello, '
105 'world!';
106
107// Collection literals
108final list = [1, 2, 3];
109final map = {'key': 'value'};
110
111// Initializing formals
112class Point {
113 final double x, y;
114 Point(this.x, this.y);
115}
116
117// Empty constructor body
118class Empty {
119 Empty(); // not Empty() {}
120}
121
122// rethrow to preserve stack trace
123try {
124 doSomething();
125} catch (e) {
126 log(e);
127 rethrow;
128}
129```
130
131- Use `whereType<T>()` to filter a collection by type.
132- Follow a **consistent rule** for `var` and `final` on local variables.
133- Initialize fields at their **declaration** when possible.
134- Override `hashCode` if you override `==`; ensure `==` obeys mathematical equality rules.
135- **Prefer specific exception handling**: use `on SomeException catch (e)` instead of broad `catch (e)` or `.catchError` handlers.
136
137---
138
139## 7. Documentation
140
141```dart
142/// Returns the sum of [a] and [b].
143///
144/// Throws [ArgumentError] if either value is negative.
145int add(int a, int b) { ... }
146```
147
148- Format comments like sentences (capitalize, end with period).
149- Use `///` doc comments — not `/* */` block comments — for types and members.
150- Prefer writing doc comments for **public APIs**; consider them for private APIs too.
151- Start doc comments with a **single-sentence summary**, separated into its own paragraph.
152- Avoid redundancy with the surrounding context.
153- Start function/method comments with a **third-person verb** if the main purpose is a side effect.
154- Start with a **noun or non-imperative verb phrase** if returning a value is the primary purpose.
155- Start **boolean** variable/property comments with "Whether" followed by a noun or gerund phrase.
156- Use `[identifier]` in doc comments to refer to in-scope identifiers.
157- Use **prose** to explain parameters, return values, and exceptions (e.g., "The [param]", "Returns", "Throws" sections).
158- Put doc comments **before** metadata annotations.
159- Document **why** code exists or how it should be used, not just what it does.
160
161---
162
163## 8. Testing Patterns
164
165- Write **unit tests** for business logic, using `group` and descriptive `test` names:
166
167```dart
168import 'package:test/test.dart';
169
170void main() {
171 group('CartService', () {
172 late CartService cart;
173
174 setUp(() => cart = CartService());
175
176 test('addItem increases item count', () {
177 cart.addItem(Product(id: '1', name: 'Widget', price: 9.99));
178 expect(cart.items, hasLength(1));
179 });
180
181 test('removeItem decreases total price', () {
182 final product = Product(id: '1', name: 'Widget', price: 9.99);
183 cart.addItem(product);
184 cart.removeItem(product.id);
185 expect(cart.totalPrice, equals(0.0));
186 });
187 });
188}
189```
190
191- Write **widget tests** using `testWidgets` and `WidgetTester`:
192
193```dart
194import 'package:flutter_test/flutter_test.dart';
195
196void main() {
197 testWidgets('LoginButton shows loading indicator when tapped',
198 (WidgetTester tester) async {
199 await tester.pumpWidget(const MaterialApp(home: LoginScreen()));
200 await tester.tap(find.byType(ElevatedButton));
201 await tester.pump();
202 expect(find.byType(CircularProgressIndicator), findsOneWidget);
203 });
204}
205```
206
207---
208
209## 9. Code Review Workflow
210
211When reviewing Dart code for Effective Dart compliance, the agent should check:
212
2131. **Naming** — verify all identifiers follow the conventions in Section 1.
2142. **Type annotations** — confirm public API parameters, return types, and uninitialized variables are annotated.
2153. **Class modifiers** — verify `final`, `sealed`, or `interface` is used where appropriate.
2164. **Documentation** — confirm all public members have `///` doc comments with a single-sentence summary.
2175. **Style** — run `dart format --output=none --set-exit-if-changed .` to verify formatting.
2186. **Analysis** — run `dart analyze` and confirm zero issues.
219
220---
221
222## References
223
224- [Effective Dart](https://dart.dev/effective-dart)
225- [Dart Site WWW GitHub Repository](https://github.com/dart-lang/site-www)