Dart
When your agent starts coding, you gotta let it cook
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.
What its author says it does
Copied from the file, not written here
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.
SKILL.md
9.0 KB, 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: