dart-modern-features
Skill2 starsChanged 30 days agoApache-2.0View on GitHub
Applies Dart 3 language features when writing or refactoring Dart and Flutter code: records, pattern matching, switch expressions, class modifiers, extension types, digit separators, wildcard variables, null-aware elements, and dot shorthands. Use when new Dart code is being written, when older Dart is being modernized, or when a review asks whether boilerplate could be replaced by a language feature. Don't use for async or isolate problems (use dart-concurrency).
1 other project uses a version of this, 1 of them edited.
Adopted by
Most recently taken up by
- kevmoo/dash_skillsedited, first seen Nov 1, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 1
- kevmoo/dash_skills★ 14814% same
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository37 other files it gives its agents.
- agy-debuggerSkill
- analyze-github-prSkill
- api-reviewSkill
- branch-cleanupSkill
- code-documentationSkill
- code-reviewSkill
- cognitive-complexitySkill
- commit-changesSkill
- context-indexerSkill
- cost-optimizationSkill
- dart-cli-app-best-practicesSkill
- dart-concurrencySkill
- designSkill
- duckdbSkill
- flutter-architectureSkill
- flutter-concurrencySkill
- flutter-testingSkill
- gh-cliSkill
- git-stackSkill
- grill-meSkill
- matcher-to-checksSkill
- merge-codeSkill
- mermaid-diagramsSkill
- monitorSkill
- nochangeSkill
- packratSkill
- planSkill
- pr-feedback-handlerSkill
- proposal-writerSkill
- pr-reviewer-recommendationSkill
- pr-reviewSkill
- sem-semantic-infoSkill
- ship-itSkill
- skill-creatorSkill
- unix-cli-best-practicesSkill
- write-pr-descriptionSkill
- write-proseSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
22 rules. About 0 are common, 11 are distinctive, and 11 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈11
Distinctive · estimate
This author's own choices. Worth a look.
11
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Use records to bundle multiple objects into a single, anonymous, immutable, aggregate structure without defining a custom class.
- Very specific: line 21
Before: Create a dedicated class just to return multiple values, or rely on weakly-typed collections like List<dynamic> or Map.
- Distinctive: line 40
After: Use records to bundle types on the fly with positional or named fields for clarity and type safety.
- Very specific: line 57
Use patterns to destructure complex data into local variables and match against specific shapes or values.
- Distinctive: line 72
After: Use patterns to combine type-checking, structural validation, and variable assignment into a single, concise statement.
- Very specific: line 85
Use switch expressions to return a value directly, eliminating the need for bulky case and break statements.
- Very specific: line 87
Before: Use switch statements that require return or assignment inside every single case block.
- Very specific: line 102
After: Use switch expressions to return evaluated values directly using the => syntax.
- Distinctive: line 119
Before: Avoid legacy abstract classes where the compiler cannot verify exhaustiveness, forcing redundant default fallback cases.
- Very specific: line 135
After: Use sealed to guarantee to the compiler that no other subclasses can exist outside this file, enabling compile-time exhaustiveness …
- Distinctive: line 153
Use extension types to establish a zero-cost wrapper around an existing type.
- Distinctive: line 155
Before: Avoid allocating new wrapper objects in memory solely to add domain-specific logic or compile-time type safety to a primitive type.
- Distinctive: line 170
After: Use extension types to compile down to the underlying type at runtime (zero overhead) while maintaining type safety and custom …
- Very specific: line 186
Use underscores (_) as digit separators in number literals to improve the visual readability of large numeric values.
- Distinctive: line 188
Before: Avoid hard-to-read, long numeric literals.
- Very specific: line 202
Use wildcards (_) as non-binding variables or parameters to explicitly signal that a value is intentionally ignored.
- Very specific: line 204
Before: Avoid inventing clunky, placeholder variable names (like ignored, unused) just to bypass compiler warnings.
- Distinctive: line 212
After: Use the underscore to explicitly drop the binding.
- Very specific: line 229
Use null-aware elements (prefixed with ?) inside collection literals (lists, sets, or maps) to conditionally include items only if they …
- Very specific: line 231
Before: Avoid using verbose collection if statements solely to perform simple null checks.
- Distinctive: line 254
Use dot shorthands to omit the explicit type name when the compiler can confidently infer it from the surrounding context.
- Distinctive: line 256
Before: Avoid fully qualifying the enum or static class name when the target type is already obvious from the context.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Dart Modern Features Guidelines
1. Features
Records
Use records to bundle multiple objects into a single, anonymous, immutable, aggregate structure without defining a custom class. Prefer them when returning multiple values from a function or temporarily grouping related data.
Before: Create a dedicated class just to return multiple values, or rely on weakly-typed collections like List<dynamic> or Map.
class UserResult {
final String name;
final int age;
UserResult(this.name, this.age);
}
UserResult fetchUser() {
return UserResult('Alice', 42);
}
void main() {
var user = fetchUser();
print(user.name); // Alice
}After: Use records to bundle types on the fly with positional or named fields for clarity and type safety.
// Returns a record with named fields.
({String name, int age}) fetchUser() {
return (name: 'Alice', age: 42);
}
void main() {
var user = fetchUser();
print(user.name); // Alice
print(user.age); // 42
}Patterns and Pattern Matching
Use patterns to destructure complex data into local variables and match against specific shapes or values. Combine them with switch statements, if-case statements, or variable declarations to unpack data directly.
Before: Validate and extract data using multiple manual type checks, null checks, and key lookups.
void processJson(Map<String, dynamic> json) {
if (json.containsKey('name') && json['name'] is String &&
json.containsKey('age') && json['age'] is int) {
String name = json['name'];
int age = json['age'];
print('$name is $age years old.');
}
}After: Use patterns to combine type-checking, structural validation, and variable assignment into a single, concise statement.
void processJson(Map<String, dynamic> json) {
// Destructure the map directly into variables.
if (json case {'name': String name, 'age': int age}) {
print('$name is $age years old.');
}
}Switch Expressions
Use switch expressions to return a value directly, eliminating the need for bulky case and break statements. Prefer them when computing and assigning a single value based on multiple conditions.
Before: Use switch statements that require return or assignment inside every single case block.
String describeStatus(int code) {
switch (code) {
case 200:
return 'Success';
case 404:
return 'Not Found';
default:
return 'Unknown';
}
}After: Use switch expressions to return evaluated values directly using the => syntax.
String describeStatus(int code) {
// Return the evaluated expression directly.
return switch (code) {
200 => 'Success',
404 => 'Not Found',
_ => 'Unknown',
};
}Class Modifiers
Apply class modifiers (sealed, final, base, interface) to restrict how classes and mixins are used outside their defining library. Use sealed to define a closed family of subtypes, allowing the compiler to enforce exhaustive switch statements without a default case.
Before: Avoid legacy abstract classes where the compiler cannot verify exhaustiveness, forcing redundant default fallback cases.
abstract class Result {}
class Success extends Result {}
class Failure extends Result {}
// Compiler doesn't know this is exhaustive.
String handle(Result r) {
if (r is Success) return 'OK';
if (r is Failure) return 'Error';
return 'Unknown'; // Required fallback
}After: Use sealed to guarantee to the compiler that no other subclasses can exist outside this file, enabling compile-time exhaustiveness checking.
// Subclasses must be defined in the same file.
sealed class Result {}
class Success extends Result {}
class Failure extends Result {}
// The compiler knows no other cases exist; no default needed!
String handle(Result r) => switch(r) {
Success() => 'OK',
Failure() => 'Error',
};Extension Types
Use extension types to establish a zero-cost wrapper around an existing type. Apply them when restricting operations on an existing type or adding custom behavior without the runtime overhead of allocating wrapper objects.
Before: Avoid allocating new wrapper objects in memory solely to add domain-specific logic or compile-time type safety to a primitive type.
class Id {
final int value;
Id(this.value);
bool get isValid => value > 0;
}
void main() {
var id = Id(42); // Allocates a new Id object
print(id.isValid);
}After: Use extension types to compile down to the underlying type at runtime (zero overhead) while maintaining type safety and custom methods at compile time.
// Wraps an int with zero runtime overhead.
extension type Id(int value) {
bool get isValid => value > 0;
}
void main() {
var id = Id(42); // At runtime, this is just an int
print(id.isValid);
}Digit Separators
Use underscores (_) as digit separators in number literals to improve the visual readability of large numeric values.
Before: Avoid hard-to-read, long numeric literals.
const int oneMillion = 1000000;After: Insert underscores to separate thousands or any other logical groupings; the compiler ignores them at compile time.
const int oneMillion = 1_000_000;Wildcard Variables
Use wildcards (_) as non-binding variables or parameters to explicitly signal that a value is intentionally ignored. Declare multiple wildcards in the same scope to prevent naming collisions and unused variable warnings.
Before: Avoid inventing clunky, placeholder variable names (like ignored, unused) just to bypass compiler warnings.
void handleEvent(String ignoredName, int status) {
print('Status: $status');
}After: Use the underscore to explicitly drop the binding. Declare multiple underscores in the same parameter list or pattern destructuring block.
// Ignore both the first and second parameters using wildcards.
void handleEvent(String _, int _, double value) {
print('Value: $value');
}
void main() {
// Destructure only the first element of a record, ignoring the rest.
var (name, _, _) = ('Alice', 42, 'Developer');
print(name); // Alice
}Null-Aware Elements
Use null-aware elements (prefixed with ?) inside collection literals (lists, sets, or maps) to conditionally include items only if they evaluate to a non-null value. Prefer this to safely build collections without manual null-filtering boilerplate.
Before: Avoid using verbose collection if statements solely to perform simple null checks.
String? optionalName = null;
var names = [
'Alice',
if (optionalName != null) optionalName,
'Charlie'
];After: Apply the ? prefix directly to elements within the collection literal for inline null-safety.
String? optionalName = null;
// The list will only contain 'Alice' and 'Charlie'.
var names = ['Alice', ?optionalName, 'Charlie'];Dot Shorthands
Use dot shorthands to omit the explicit type name when the compiler can confidently infer it from the surrounding context. Apply this to reduce boilerplate when working with enums, static fields, and named constructors.
Before: Avoid fully qualifying the enum or static class name when the target type is already obvious from the context.
enum LogLevel { info, warning, error }
LogLevel currentLevel = LogLevel.info;After: Omit the class prefix; the compiler infers the type, saving keystrokes and reducing visual noise.
enum LogLevel { info, warning, error }
// The compiler infers LogLevel from the variable declaration context.
LogLevel currentLevel = .info;Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

