mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-04 08:13:30 +08:00
feat: add C# and Dart language support
This commit is contained in:
159
rules/dart/coding-style.md
Normal file
159
rules/dart/coding-style.md
Normal file
@@ -0,0 +1,159 @@
|
||||
---
|
||||
paths:
|
||||
- "**/*.dart"
|
||||
- "**/pubspec.yaml"
|
||||
- "**/analysis_options.yaml"
|
||||
---
|
||||
# Dart/Flutter Coding Style
|
||||
|
||||
> This file extends [common/coding-style.md](../common/coding-style.md) with Dart and Flutter-specific content.
|
||||
|
||||
## Formatting
|
||||
|
||||
- **dart format** for all `.dart` files — enforced in CI (`dart format --set-exit-if-changed .`)
|
||||
- Line length: 80 characters (dart format default)
|
||||
- Trailing commas on multi-line argument/parameter lists to improve diffs and formatting
|
||||
|
||||
## Immutability
|
||||
|
||||
- Prefer `final` for local variables and `const` for compile-time constants
|
||||
- Use `const` constructors wherever all fields are `final`
|
||||
- Return unmodifiable collections from public APIs (`List.unmodifiable`, `Map.unmodifiable`)
|
||||
- Use `copyWith()` for state mutations in immutable state classes
|
||||
|
||||
```dart
|
||||
// BAD
|
||||
var count = 0;
|
||||
List<String> items = ['a', 'b'];
|
||||
|
||||
// GOOD
|
||||
final count = 0;
|
||||
const items = ['a', 'b'];
|
||||
```
|
||||
|
||||
## Naming
|
||||
|
||||
Follow Dart conventions:
|
||||
- `camelCase` for variables, parameters, and named constructors
|
||||
- `PascalCase` for classes, enums, typedefs, and extensions
|
||||
- `snake_case` for file names and library names
|
||||
- `SCREAMING_SNAKE_CASE` for constants declared with `const` at top level
|
||||
- Prefix private members with `_`
|
||||
- Extension names describe the type they extend: `StringExtensions`, not `MyHelpers`
|
||||
|
||||
## Null Safety
|
||||
|
||||
- Avoid `!` (bang operator) — prefer `?.`, `??`, `if (x != null)`, or Dart 3 pattern matching; reserve `!` only where a null value is a programming error and crashing is the right behaviour
|
||||
- Avoid `late` unless initialization is guaranteed before first use (prefer nullable or constructor init)
|
||||
- Use `required` for constructor parameters that must always be provided
|
||||
|
||||
```dart
|
||||
// BAD — crashes at runtime if user is null
|
||||
final name = user!.name;
|
||||
|
||||
// GOOD — null-aware operators
|
||||
final name = user?.name ?? 'Unknown';
|
||||
|
||||
// GOOD — Dart 3 pattern matching (exhaustive, compiler-checked)
|
||||
final name = switch (user) {
|
||||
User(:final name) => name,
|
||||
null => 'Unknown',
|
||||
};
|
||||
|
||||
// GOOD — early-return null guard
|
||||
String getUserName(User? user) {
|
||||
if (user == null) return 'Unknown';
|
||||
return user.name; // promoted to non-null after the guard
|
||||
}
|
||||
```
|
||||
|
||||
## Sealed Types and Pattern Matching (Dart 3+)
|
||||
|
||||
Use sealed classes to model closed state hierarchies:
|
||||
|
||||
```dart
|
||||
sealed class AsyncState<T> {
|
||||
const AsyncState();
|
||||
}
|
||||
|
||||
final class Loading<T> extends AsyncState<T> {
|
||||
const Loading();
|
||||
}
|
||||
|
||||
final class Success<T> extends AsyncState<T> {
|
||||
const Success(this.data);
|
||||
final T data;
|
||||
}
|
||||
|
||||
final class Failure<T> extends AsyncState<T> {
|
||||
const Failure(this.error);
|
||||
final Object error;
|
||||
}
|
||||
```
|
||||
|
||||
Always use exhaustive `switch` with sealed types — no default/wildcard:
|
||||
|
||||
```dart
|
||||
// BAD
|
||||
if (state is Loading) { ... }
|
||||
|
||||
// GOOD
|
||||
return switch (state) {
|
||||
Loading() => const CircularProgressIndicator(),
|
||||
Success(:final data) => DataWidget(data),
|
||||
Failure(:final error) => ErrorWidget(error.toString()),
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
- Specify exception types in `on` clauses — never use bare `catch (e)`
|
||||
- Never catch `Error` subtypes — they indicate programming bugs
|
||||
- Use `Result`-style types or sealed classes for recoverable errors
|
||||
- Avoid using exceptions for control flow
|
||||
|
||||
```dart
|
||||
// BAD
|
||||
try {
|
||||
await fetchUser();
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
|
||||
// GOOD
|
||||
try {
|
||||
await fetchUser();
|
||||
} on NetworkException catch (e) {
|
||||
log('Network error: ${e.message}');
|
||||
} on NotFoundException {
|
||||
handleNotFound();
|
||||
}
|
||||
```
|
||||
|
||||
## Async / Futures
|
||||
|
||||
- Always `await` Futures or explicitly call `unawaited()` to signal intentional fire-and-forget
|
||||
- Never mark a function `async` if it never `await`s anything
|
||||
- Use `Future.wait` / `Future.any` for concurrent operations
|
||||
- Check `context.mounted` before using `BuildContext` after any `await` (Flutter 3.7+)
|
||||
|
||||
```dart
|
||||
// BAD — ignoring Future
|
||||
fetchData(); // fire-and-forget without marking intent
|
||||
|
||||
// GOOD
|
||||
unawaited(fetchData()); // explicit fire-and-forget
|
||||
await fetchData(); // or properly awaited
|
||||
```
|
||||
|
||||
## Imports
|
||||
|
||||
- Use `package:` imports throughout — never relative imports (`../`) for cross-feature or cross-layer code
|
||||
- Order: `dart:` → external `package:` → internal `package:` (same package)
|
||||
- No unused imports — `dart analyze` enforces this with `unused_import`
|
||||
|
||||
## Code Generation
|
||||
|
||||
- Generated files (`.g.dart`, `.freezed.dart`, `.gr.dart`) must be committed or gitignored consistently — pick one strategy per project
|
||||
- Never manually edit generated files
|
||||
- Keep generator annotations (`@JsonSerializable`, `@freezed`, `@riverpod`, etc.) on the canonical source file only
|
||||
Reference in New Issue
Block a user