Standard: Dart Coding Standards
Baseline rules for all Dart code. Enforced by flutter analyze with a strict lint set (very_good_analysis or flutter_lints) plus dart format. These are law — generated code must conform. UI-specific rules live in flutter_standards.md.
Naming
- Types, enums, extensions
UpperCamelCase; members, variables, parameterslowerCamelCase. - Files and directories
snake_case.dart. - Booleans read as assertions:
isEnabled,hasLoaded,canSubmit. - Methods are verb phrases (
fetchAccounts()); nogetprefix on accessors. - Private members use a leading underscore; libraries expose an explicit public surface.
dart
// ✅ // ❌
class AccountSummary {} class account_summary {}
Future<void> loadTransactions() async {} void getTxns() {}
final bool isAuthenticated; final bool auth;Types & Immutability
- Prefer
finalovervar; preferconstwherever the value is compile-time constant. - Model entities as immutable classes with
constconstructors andcopyWith. - Use
sealedclasses for finite state and result unions, thenswitchon them — the compiler enforces exhaustiveness. Do not use loose booleans or strings for state. - Use
abstract interface classfor contracts that must be implemented, not extended. - Annotate public APIs with explicit types; do not rely on inference across a library boundary.
dart
// ✅ exhaustive by construction
sealed class Result<T> {
const Result();
}
final class Ok<T> extends Result<T> {
const Ok(this.value);
final T value;
}
final class Err<T> extends Result<T> {
const Err(this.failure);
final Failure failure;
}Null Safety
- No
!(bang) on data that can legitimately be absent. It is a runtime crash waiting for a bad payload. Use?.,??, a pattern match, or an early return. lateonly for a value guaranteed initialized before first read, and never for something that might legitimately stay unset.- Do not widen a type to nullable to silence the analyzer; fix the model.
dart
// ✅ // ❌
final name = user?.name ?? 'Guest'; final name = user!.name;
if (token case final String t) use(t); use(token!);Errors
- Cross layer boundaries with typed failures — a sealed
Failureunion or aResult— not raw exceptions from a package. - Catch narrowly (
on DioException,on FormatException), never a barecatch (_) {}. - Every
catcheither handles, translates, or rethrows. Silently swallowing is a defect. throwonly for genuinely exceptional conditions; expected failure paths return a value.- Never include a token, password, or PII in an exception message.
dart
// ✅ // ❌
} on DioException catch (e) { } catch (_) {
throw _mapToFailure(e); return [];
} }Async & Concurrency
async/awaitover raw.then()chains. Alwaysawaitor deliberately handle aFuture— an unawaited future swallows its error.- Use
unawaited()explicitly when fire-and-forget is intended, so the intent is visible. Futurefor one value,Streamfor many. Always cancel subscriptions (ref.onDispose,dispose()).- Move CPU-bound work to an isolate (
Isolate.run/compute). Anything over a few milliseconds on the UI isolate drops frames. - Never use
Future.delayedas a synchronization mechanism; await the actual operation. - Guard
BuildContextuse after anawaitwithif (!context.mounted) return;.
Logging & Secrets
- No
print()in shipped code — use a logger with levels, and disable verbose logging in release. - Never log tokens, passwords, auth headers, or PII, in any build mode.
- Log actionable context (operation, identifier, failure kind), not whole payloads.
Tooling
dart formatis the formatting authority — no hand-formatting debates. CI runsdart format --set-exit-if-changed ..flutter analyzemust be clean; an// ignore:needs a comment explaining why.- Enable
prefer_const_constructors— it is a rendering-performance rule, not a style rule (see../skills/performance/flutter/rendering_optimization.md). - Pin dependencies with a committed
pubspec.lockfor applications.
Related
flutter_standards.md— widgets, state, navigation, theming, a11y.architecture_standards.md— layers, SOLID, DI.security_standards.md— OWASP MASVS baseline.testing_standards.md— pyramid, FIRST, coverage, doubles.