Agent skill
Dart
Execute these commands after EVERY implementation (see AGENT_AUTOMATION module for full workflow).
Install this agent skill to your Project
npx add-skill https://github.com/hivellm/rulebook/tree/main/templates/skills/languages/dart
SKILL.md
Dart Project Rules
Agent Automation Commands
CRITICAL: Execute these commands after EVERY implementation (see AGENT_AUTOMATION module for full workflow).
# Complete quality check sequence:
dart format --set-exit-if-changed . # Format check
dart analyze --fatal-infos # Linting + analysis
dart test # All tests (100% pass)
dart test --coverage=coverage # Coverage check
# Security audit:
dart pub outdated # Check outdated deps
Dart Configuration
CRITICAL: Use Dart 3.0+ with sound null safety and strict analysis.
- Version: Dart 3.0+
- Recommended: Dart 3.3+
- Null Safety: Sound null safety required
- Analysis: dart analyze with strict mode
- Testing: package:test
pubspec.yaml Requirements
name: your_package
description: A comprehensive package description
version: 0.1.0
homepage: https://github.com/you/your_package
environment:
sdk: '>=3.0.0 <4.0.0'
dependencies:
http: ^1.1.0
dev_dependencies:
test: ^1.24.0
lints: ^3.0.0
coverage: ^1.7.0
dart_code_metrics: ^5.7.0
analysis_options.yaml Requirements
include: package:lints/recommended.yaml
analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true
errors:
invalid_annotation_target: error
missing_required_param: error
missing_return: error
exclude:
- build/**
- .dart_tool/**
linter:
rules:
- always_declare_return_types
- always_require_non_null_named_parameters
- avoid_print
- avoid_relative_lib_imports
- avoid_returning_null_for_void
- avoid_slow_async_io
- avoid_types_as_parameter_names
- cancel_subscriptions
- close_sinks
- prefer_const_constructors
- prefer_final_fields
- prefer_final_locals
- sort_constructors_first
- unawaited_futures
Code Quality Standards
Mandatory Quality Checks
CRITICAL: After implementing ANY feature, you MUST run these commands in order.
IMPORTANT: These commands MUST match your GitHub Actions workflows to prevent CI/CD failures!
# Pre-Commit Checklist (MUST match .github/workflows/*.yml)
# 1. Format check (matches workflow - use --set-exit-if-changed!)
dart format --set-exit-if-changed --line-length 100 .
# 2. Analyze (MUST pass with no issues - matches workflow)
dart analyze --fatal-infos --fatal-warnings
# 3. Run all tests (MUST pass 100% - matches workflow)
dart test
# 4. Check coverage (MUST meet threshold - matches workflow)
dart test --coverage=coverage
dart pub global activate coverage
dart pub global run coverage:format_coverage \
--lcov --in=coverage --out=coverage/lcov.info --report-on=lib
# 5. Metrics check (matches workflow)
dart pub global activate dart_code_metrics
dart pub global run dart_code_metrics:metrics analyze lib
# If ANY fails: ❌ DO NOT COMMIT - Fix first!
If ANY of these fail, you MUST fix the issues before committing.
Why This Matters:
- Running different commands locally than in CI causes "works on my machine" failures
- CI/CD workflows will fail if commands don't match
- Example: Using
dart format .locally butdart format --set-exit-if-changedin CI = failure - Example: Missing
--fatal-infosflag = warnings pass locally but fail in CI
Testing
- Framework: package:test
- Location:
/testdirectory - Coverage: package:coverage (80%+ threshold)
- Naming: test files must end with
_test.dart
Example test structure:
import 'package:test/test.dart';
import 'package:your_package/your_package.dart';
void main() {
group('DataProcessor', () {
late DataProcessor processor;
setUp(() {
processor = DataProcessor(threshold: 0.5);
});
tearDown(() {
// Cleanup
});
test('processes valid input correctly', () {
final input = [1, 2, 3, 4, 5];
final result = processor.process(input);
expect(result, isNotEmpty);
expect(result.length, greaterThan(0));
});
test('handles empty input', () {
final result = processor.process([]);
expect(result, isEmpty);
});
test('throws on null input', () {
expect(
() => processor.process(null as List<int>),
throwsA(isA<ArgumentError>()),
);
});
});
group('Validator', () {
test('validates correct data', () {
expect(Validator.isValid('test'), isTrue);
});
test('rejects invalid data', () {
expect(Validator.isValid(''), isFalse);
});
});
}
Null Safety
- REQUIRED: Sound null safety for all code
- Use
?for nullable types - Use
!operator sparingly - Prefer null-aware operators
Example:
// ✅ GOOD: Proper null safety
class User {
final String id;
final String name;
final String? email; // Nullable
final int? age; // Nullable
User({
required this.id,
required this.name,
this.email,
this.age,
});
String getDisplayName() {
return email != null ? '$name <$email>' : name;
}
int getAgeOrDefault() => age ?? 0;
}
// ❌ BAD: Unsafe null handling
class User {
String id;
String name;
String email; // Should be nullable but isn't
User(this.id, this.name, this.email);
String getDisplayName() {
return '$name <$email>'; // Crash if email is null!
}
}
Async Programming
- Always use
async/await - Handle Future errors with try/catch
- Use
unawaited()to mark intentionally unawaited futures
Example:
// ✅ GOOD: Proper async handling
Future<List<User>> fetchUsers() async {
try {
final response = await http.get(Uri.parse('https://api.example.com/users'));
if (response.statusCode != 200) {
throw HttpException('Failed to load users: ${response.statusCode}');
}
final List<dynamic> data = jsonDecode(response.body);
return data.map((json) => User.fromJson(json)).toList();
} catch (e) {
print('Error fetching users: $e');
rethrow;
}
}
// ❌ BAD: No error handling
Future<List<User>> fetchUsers() async {
final response = await http.get(Uri.parse('https://api.example.com/users'));
final List<dynamic> data = jsonDecode(response.body); // Could crash!
return data.map((json) => User.fromJson(json)).toList();
}
Best Practices
DO's ✅
- USE sound null safety
- USE const constructors when possible
- PREFER final for variables
- DOCUMENT public APIs
- HANDLE errors explicitly
- TEST all code paths
- USE meaningful names
- FOLLOW dart_style guidelines
DON'Ts ❌
- NEVER use
dynamicunless absolutely necessary - NEVER suppress analyzer warnings without justification
- NEVER use
print()in production code (use logging) - NEVER ignore lint warnings
- NEVER use deprecated APIs
- NEVER commit with failing tests
- NEVER use
!operator without null check
CI/CD Requirements
Must include GitHub Actions workflows:
-
Testing (
dart-test.yml):- Test on ubuntu-latest, windows-latest, macos-latest
- Dart versions: stable, beta
- Upload coverage
-
Linting (
dart-lint.yml):- dart format --set-exit-if-changed
- dart analyze --fatal-infos --fatal-warnings
-
Build (
dart-build.yml):- dart compile exe (for CLI apps)
- Verify output artifacts
Publishing to pub.dev
Prerequisites
- Create account at https://pub.dev
- Run
dart pub login - Verify package name available
Publishing Workflow
# 1. Update version in pubspec.yaml
# 2. Update CHANGELOG.md
# 3. Run all quality checks
dart format --set-exit-if-changed .
dart analyze --fatal-infos --fatal-warnings
dart test
# 4. Dry run
dart pub publish --dry-run
# 5. Publish
dart pub publish
# 6. Create git tag
git tag -a v1.0.0 -m "Release 1.0.0"
Didn't find tool you were looking for?