Dart
Dart 3.x language standards and code quality conventions. Use when writing or reviewing any Dart code — null safety, patterns, sealed classes, records, class modifiers, naming, immutability, collections, async, and import organisation.From its SKILL.md
npx -y skills add ndisisnd/cook --skill dartAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.0 KB, ~2.1k tokens by cl100k_base, as published. Nobody here has run it
Dart Standards
Priority: P0 — Language Correctness
Null Safety
- Avoid
!. Prefer local promotion, null-check patterns, and privatefinalfields. - Use
!only for documented invariants or framework/external boundaries where non-null is guaranteed but not expressible to the analyzer. - Prefer
?.,??, and null-aware patterns over forced unwrapping. AVOID lateif you need to check whether the variable was initialised — use nullable + null-check instead.DON'Texplicitly initialise variables tonull; let the type system express optionality.
Immutability
- Use
const>final>var. Use@freezedfor data classes. - Prefer
finalfor all class members. Usevaronly for locally-obvious short-lived locals. AVOIDpubliclate finalfields without initializers.
Pattern Matching (Dart 3.x)
Use switch expressions with exhaustive patterns and destructuring. Supported pattern types:
| Pattern | Example |
|---|---|
| Constant | case 42: |
| Variable | case var x: |
| Wildcard | case _: |
| Object | case Circle(radius: var r): |
| Record | case (String name, int age): |
| List | case [first, ...rest]: |
| Map | case {'key': var v}: |
| Logical-or | `case 1 |
| Guard | case var x when x > 0: |
String describe(Shape s) => switch (s) {
Circle(radius: var r) when r > 10 => 'large circle',
Circle(radius: var r) => 'circle r=$r',
Rectangle(width: var w, height: var h) => '${w}x$h rect',
};
Records
- Use records for returning multiple values:
(String, int). - Use named fields for clarity beyond two elements:
({String name, int age}).
Class Modifiers (Dart 3.x)
Choose the right modifier to express API intent explicitly:
| Modifier | Extends outside lib | Implements outside lib | Use for |
|---|---|---|---|
sealed | no | no | Exhaustive domain state (enables exhaustive switch) |
final | no | no | Closed hierarchy — no extension or implementation |
base | yes | no | Allow inheritance, prevent external implementation |
interface | no | yes | Pure contracts — implementation only |
sealedis implicitly abstract; direct subtypes must be in the same library for exhaustive switching.- Subclasses of a
sealedclass are not implicitly abstract — mark each subtype intentionally. - Use
finalinstead ofsealedwhen you want to close external subtyping but still add subtypes later without breaking exhaustive switches.
sealed class AuthState {}
final class Authenticated extends AuthState { final User user; Authenticated(this.user); }
final class Unauthenticated extends AuthState {}
Mixins
- Use
mixinfor behaviour shared across unrelated class hierarchies. - Use
mixin class(Dart 3.0) when the type must also be usable as a standalone class. - Prefer
mixinoverabstract classwhen no constructor is needed.
Enhanced Enums (Dart 2.17+)
Enums can have fields, constructors, and methods. Prefer over utility classes with static constants.
enum Status {
active('Active'),
inactive('Inactive');
const Status(this.label);
final String label;
}
Extensions
- Use
extensionto add utility methods to third-party or built-in types. - Always name extensions (
extension StringX on String) — unnamed extensions are harder to import selectively.
Wildcards (Dart 3.7+)
Use _ for unused variables in declarations and patterns.
Async
- Prefer
async/awaitover rawFuture.then. - Use
unawaited()for intentional fire-and-forget; never silently discard a future. DON'Tmark a functionasyncif it contains noawait— it adds overhead with no benefit.AVOIDusingCompleterdirectly; preferasync/awaitorStreamController.AVOIDFutureOr<T>as a return type.AVOIDreturning nullableFuture,Stream, or collection types from public APIs.- Cancel
StreamSubscriptions and close ownedStreamControllers orSinks. - Avoid
async voidexcept for framework callbacks that requirevoid.
Error Handling
- Use
on ExceptionType catch (e)— never barecatchwithouton(swallows everything). DON'Tsilently discard caught errors.- Throw
Errorsubclasses only for programmatic errors (bugs). UseExceptionfor recoverable runtime conditions. - Use
rethrowto re-propagate after partial handling; never re-throw the caught object manually. - Use
assert()for development-time invariants — stripped in production.
Types
- No
dynamic. UseObject,Object?, or generics. - Annotate return types and parameter types on all public declarations.
DON'Tredundantly annotate initialised local variables — let inference work.- Use
typedeffor named type aliases (typedef UserId = String). Prefer inline function type syntax in parameter positions over typedef.
Members & Constructors
- Use initializing formals:
const User({required this.name}). - Use
;not{}for empty constructor bodies. - Never use
new. DON'Tusethis.except to redirect constructors or avoid shadowing.DON'Tperform complex calculations or async work inside constructors.- Use a getter for pure computations:
int get invoiceTotal =>notint calcTotal().
Equality
- If you override
operator ==, overridehashCode. - Equality must be reflexive, symmetric, transitive, and stable over time.
- Avoid custom equality on mutable classes; prefer immutable value types.
- Use
identical(this, other)as the fast path before structural comparison.
Priority: P1 — Style & Conventions
Naming
- Types and extensions:
UpperCamelCase - Members, variables, parameters:
lowerCamelCase - Files, packages, directories:
lowercase_with_underscores - Import prefixes:
lowercase_with_underscores - Constants: prefer
lowerCamelCase(notSCREAMING_CAPS) unless matching generated or existing code style. - Capitalise acronyms longer than two letters as words:
HttpRequest,parseUrl DON'Tuse a leading_on non-private identifiers.- Name value-object converters for their target context:
get apiFilterTypenotget filterType.
Scoping
- No top-level mutable state. Encapsulate in a class or inject via DI.
- Library-private identifiers use
_prefix.
Strings
- Prefer single quotes. Use double quotes only when the string itself contains a single quote.
- Prefer interpolation over concatenation:
'Hello $name'not'Hello ' + name. - Adjacent string literals can be concatenated without
+. - Omit curly braces in interpolation unless required:
'$name'not'${name}'.
Trailing Commas
Always use trailing commas for multi-line argument lists and collection literals.
Expression Bodies
Prefer => for single-expression functions and getters.
Collections
- Use
.isEmpty/.isNotEmpty— never.length == 0. - Use collection
if,for, and spread...for composable collections. - Type empty collections explicitly:
<String>[],<String, User>{}. - Prefer
.map,.where,.fold,.anyover manual loops where clarity wins. - Use
.firstOrNull,.lastOrNull,.elementAtOrNull(i)for safe indexed access. DON'TuseIterable.forEach()with a function literal — useforloops or tear-offs.DON'Tusecast()when a nearby operation will do.
Imports
- Group order:
dart:→package:→ relative. Sort each section alphabetically. - Use relative imports for intra-package files; never
package:app/...within the same package. - Specify exports in a separate section after all imports.
Tear-offs
Prefer list.forEach(print) over list.forEach((e) => print(e)).
Anti-Patterns
!without a documented invariant, local promotion alternative, or framework boundary- Overriding
==withouthashCode - Custom equality on mutable classes
varfor class membersdynamicanywhereasyncon a function with noawaitasync voidoutside framework callbacks- Leaked
StreamSubscription,StreamController, orSink - Bare
catchwithouton - Global mutable state
newkeyword- Package imports within the same package
FutureOr<T>as a return type- Logic or async work inside constructors
- Zero-argument methods for pure computations — use a getter
References
Load only what the current task requires:
What ships with it: 3 files
7.8 KB alongside SKILL.md
refs/
- testing.md3.2 KB
- tooling.md3.6 KB
- _INDEX.md975 B