Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when writing or refactoring Dart/Flutter code: apply the non-default Effective Dart conventions the base model gets wrong.
.claude/skills/effective-dart/SKILL.md| Model | Eval pass | Runs |
|---|---|---|
| gemini-3.6-flash | 100% | 4 |
| Model | Lift | Δ tokens | Δ turns | Cases | Verified |
|---|---|---|---|---|---|
| gemini-3.6-flashbest | +22% | +195% | 0% | 23 | 54d ago |
| gemini-3.5-flash | +8% | — | 0% | 24 | 86d ago |
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-10 | ✗→✓ | ▲ Improved | — | — |
| case-20 | ✗→✓ | ▲ Improved | — | — |
| case-05 | ✗→✓ | ▲ Improved | — | — |
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-23 | ✗→✓ | ▲ Improved | — | — |
Enforces the arbitrary Effective Dart spellings, keywords, and shapes that a model does NOT produce by default. Apply to every piece of Dart/Flutter code you write or refactor: identifiers, error handling, strings, collections, async signatures, and doc comments.
Treat an acronym/abbreviation longer than two letters as a single word: only its first letter is capitalized inside UpperCamelCase, and it is all-lowercase inside lowerCamelCase.
JsonParser, UrlBuilder, HtmlSanitizer, CsvExporter, PdfGenerator, XmlValidator,ApiClient, HttpRequest, RestApiClient. NEVER JSONParser, URLBuilder, HTMLSanitizer, CSVExporter, PDFGenerator, XMLValidator, APIClient, HTTPRequest.
lowerCamelCase the acronym is lowercase when leading (jsonBody, apiClient,httpResponse) and word-cased when interior (parseJsonBody, buildUrl, sendHttpRequest).
rethrow, never throw e;To propagate the caught exception unchanged, use the bare keyword rethrow. throw e; discards the original stack trace. Logging-then-propagating is } on X catch (e) { log(e); rethrow; }.
on clauseHandle a specific exception with on FormatException catch (e). Do NOT use a bare catch (e) (it swallows every error type) or .catchError(...) when you only mean to handle one kind.
+Break a long string literal across lines by placing two or more quoted strings side by side; the compiler concatenates them at no runtime cost. NEVER join the pieces with the + operator.
whereType<T>()To keep only the elements of a type, call whereType<T>(). Do NOT write .where((e) => e is T) and do NOT chain .where(...).cast<T>().
Future<void>An async method that produces no value declares return type Future<void>. NEVER bare void, bare Future, or Future<Null>.
A documentation comment on a boolean property/getter starts with the word Whether, e.g. /// Whether the token has expired. NEVER "Returns true if…" or "Checks if…".
/// doc comments and [bracket] referencesDocument public APIs with /// (never /* */ block comments, never plain //). Refer to parameters, return values, and exception types in prose with square brackets: [a], [id], [ArgumentError]. The first line is a single-sentence summary ending in a period; doc comments go before any metadata annotation (@override).
;, never {}A constructor with no body is Logger();, not Logger() {}.
getX() methodsA conceptual property read is a getter: String get fullName => '$first $last';. NEVER expose it as a getFullName() method.
UpperCamelCase; files/dirs → lowercase_with_underscores;variables/params/functions → lowerCamelCase.
final over var when a local never changes; const for compile-time constants.R1 acronym casing
dart// BEFORE class JSONParser { Model parseJSON(String body) => ...; } // AFTER class JsonParser { Model parseJson(String body) => ...; }
R2 rethrow
dart// BEFORE try { read(); } catch (e) { log(e); throw e; } // AFTER try { read(); } catch (e) { log(e); rethrow; }
R3 typed on clause
dart// BEFORE try { return int.parse(s); } catch (e) { return null; } // AFTER try { return int.parse(s); } on FormatException catch (_) { return null; }
R4 adjacent string literals
dart// BEFORE const help = 'Usage: tool [options] ' + 'run the pipeline ' + 'and exit.'; // AFTER const help = 'Usage: tool [options] ' 'run the pipeline ' 'and exit.';
R5 whereType
dart// BEFORE final ints = values.where((e) => e is int).cast<int>(); // AFTER final ints = values.whereType<int>();
R6 Future<void>
dart// BEFORE Future saveSettings() async { await disk.write(data); } // AFTER Future<void> saveSettings() async { await disk.write(data); }
R7 "Whether" docs
dart// BEFORE /// Returns true if the token has expired. bool get isExpired => DateTime.now().isAfter(expiry); // AFTER /// Whether the token has expired. bool get isExpired => DateTime.now().isAfter(expiry);
R8 /// + bracket refs
dart// BEFORE /* Divides a by b. Throws if b is zero. */ int divide(int a, int b) => ...; // AFTER /// Returns the quotient of [a] divided by [b]. /// /// Throws [ArgumentError] if [b] is zero. int divide(int a, int b) => ...;
R9 empty constructor
dart// BEFORE class Logger { Logger() {} } // AFTER class Logger { Logger(); }
R10 getter not getX()
dart// BEFORE String getFullName() => '$firstName $lastName'; // AFTER String get fullName => '$firstName $lastName';
IO stays fully capitalized(IOSink). A two-letter abbreviation like ID is word-cased (Id, userId, not userID). The "word-case it" rule kicks in strictly for length > 2 (Http, Json).
rethrow only re-raises the in-flight exception. If you must throw a different error, that isa genuine throw NewError(...) — R2 forbids throw e; (the same object), not deliberately raising a new one.
whereType vs cast. Use whereType<T>() to select elements of a type; cast<T>() is forasserting an already-homogeneous list. Don't reach for cast to do filtering.
Future<void> vs void. A synchronous callback that returns nothing is void. Only anasync member (or one returning a future) uses Future<void>.
[a] works because a is a parameter; don't bracketarbitrary prose words that aren't real identifiers.
/* */license banner is not a doc comment and is exempt.
JSONParser, URLBuilder, APIClient. DO write JsonParser, UrlBuilder, ApiClient.throw e;. DO rethrow;.catch (e) to handle one kind. DO on FormatException catch (e).'a' + 'b' across lines. DO adjacent literals 'a' 'b'..where((e) => e is T). DO whereType<T>().void/Future from a no-value async member. DO Future<void>./* */. DO use /// with [bracket] references.Logger() {}. DO write Logger();.getFullName(). DO expose String get fullName.throw e; out of habit from other languages, losing the stack trace.catch (e) "to be safe," which hides unrelated bugs.+ as in Java/JS..where(...is T) instead of the built-in whereType.void/Future on an async no-value method.getX() accessor methods instead of Dart getters.Json, Url, Api, Http, Html, Csv, Pdf, Xml).rethrow (not throw e;); typed on X catch (not bare catch).+).whereType<T>() for type filtering.Future<void>./// + [bracket] refs.Name();; property reads are getters.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-10 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-23 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +22 percentage points is the difference between those two pass rates over the 23 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
| Model | Method | Date | Lift |
|---|---|---|---|
| gemini-3.5-flash | verified | 6/27/2026 | +8% |
Other measured skills in the registry, with their headline benchmark lift.