dart-api-design
SKILL.md
Dart Effective Design
Goal
Analyzes and refactors Dart code to enforce idiomatic API design, strict type safety, and robust object-oriented hierarchies. Applies Dart 3 class modifiers to control extension and implementation boundaries, ensures proper encapsulation through getters and setters, and standardizes naming conventions for predictable, maintainable libraries.
Instructions
-
Apply Naming Conventions
- Use noun phrases for non-boolean properties/variables (e.g.,
list.length). - Use non-imperative verb phrases for boolean properties (e.g.,
isEmpty,canClose). - Use imperative verbs for methods with side effects (e.g.,
list.add()). - Use
to___()for methods that copy state to a new object (e.g.,list.toSet()). - Use
as___()for methods returning a different representation backed by the original object (e.g.,map.asMap()). - Follow standard generic type parameter mnemonics:
E(elements),K/V(key/value),R(return type),T/S/U(general).
- Use noun phrases for non-boolean properties/variables (e.g.,
-
Determine Class Modifiers (Decision Logic) Evaluate every class declaration against the intended API boundary.
- Does the class require a full, concrete implementation of its interface?
- No: Use
abstract class.
- No: Use
- Should external libraries be allowed to implement the class interface, but NOT inherit its implementation?
- Yes: Use
interface class(orabstract interface classfor pure interfaces).
- Yes: Use
- Should external libraries be allowed to inherit the implementation, but NOT implement the interface?
- Yes: Use
base class. (Note: Subclasses must be markedbase,final, orsealed).
- Yes: Use
- Is the class part of a closed, enumerable set of subtypes for exhaustive switching?
- Yes: Use
sealed class.
- Yes: Use
- Should the class be completely locked from external extension and implementation?
- Yes: Use
final class.
- Yes: Use
- Does the class contain only a single abstract method (e.g.,
call)?- Yes: Convert to a
typedeffunction type.
- Yes: Convert to a
- Does the class contain only static members?
- Yes: Move members to top-level library declarations.
- STOP AND ASK THE USER: If the extension/implementation intent of a public class is ambiguous, pause and ask the user to clarify the intended API boundary before applying modifiers.
- Does the class require a full, concrete implementation of its interface?
-
Enforce Parameter Rules
- Prefer named parameters for functions with more than two arguments.
- Avoid positional boolean parameters.
- Omit the verb (
is,can,has) for named boolean parameters. - Use inclusive start and exclusive end parameters for ranges.
// GOOD void configure(String name, {bool paused = false, bool growable = true}) {} var sub = items.sublist(0, 3); // BAD void configure(String name, bool isPaused, bool canGrow) {} -
Manage State and Encapsulation
- Prefer
finalfor fields and top-level variables. - Avoid public fields; use getters and setters for encapsulation.
- Do not define a setter without a corresponding getter.
- Avoid public
late finalfields without initializers (this implicitly exposes a setter). - Use getters for operations that conceptually access properties (no arguments, returns a result, idempotent, no user-visible side effects).
// GOOD class Rectangle { double _width; Rectangle(this._width); double get width => _width; set width(double value) => _width = value; double get area => _width * _width; // Idempotent, no side effects } - Prefer
-
Apply Strict Type Annotations
- Annotate variables without initializers.
- Annotate fields and top-level variables if the type isn't obvious.
- Annotate return types and parameter types on non-local function declarations.
- Do NOT redundantly annotate initialized local variables.
- Do NOT annotate inferred parameter types on function expressions (closures).
- Do NOT type annotate initializing formals (
this.x). - Use inline function types over legacy typedefs.
- Use
Future<void>(notvoidorFuture<Null>) for async members that do not produce values. - Avoid
FutureOr<T>as a return type; returnFuture<T>instead.
// GOOD typedef Comparison<T> = int Function(T a, T b); class FilteredObservable { final bool Function(Event) _predicate; // Inline function type FilteredObservable(this._predicate); } -
Implement Equality Safely
- If overriding
==, you MUST overridehashCode. - Ensure
==is reflexive, symmetric, and transitive. - Avoid defining custom equality for mutable classes.
- Do not make the parameter to
==nullable (Objectinstead ofObject?).
// GOOD class Person { final String name; Person(this.name); bool operator ==(Object other) => other is Person && name == other.name; int get hashCode => name.hashCode; } - If overriding
Constraints
- No Positional Booleans: Never generate function signatures with positional boolean arguments.
- No Legacy Typedefs: Never use the
typedef name(args)syntax. Always usetypedef Name = ReturnType Function(args). - No Faked Overloading: Do not use runtime type tests (
is) to fake method overloading. Create distinctly named methods instead. - No Nullable Collections: Avoid returning nullable
Future,Stream, or collection types. Return empty collections instead. - No Fluent
this: Avoid returningthisfrom methods just to enable a fluent interface; rely on Dart's cascade operator (..) instead. - No Raw Types: Never write incomplete generic types (e.g.,
List numbers = []). Always specify type arguments or rely on complete inference. - Related Skills:
dart-effective-style,dart-package-management.
Weekly Installs
14
Repository
dart-lang/skillsGitHub Stars
6
First Seen
Mar 17, 2026
Security Audits
Installed on
amp11
cline11
opencode11
cursor11
kimi-cli11
codex11