Dart Best Practices
Habits around null safety, immutability, naming, and tooling that separate idiomatic Dart from code that merely compiles.
読了時間 3 分
You've now covered the language end to end — types, control flow, functions, collections, null safety, classes, generics, and async code. This closing lesson is less about new syntax and more about the habits that make Dart code feel idiomatic rather than merely correct.
Prefer final over var, and var over explicit types
// Prefer this
final user = fetchUser();
// Over this, when reassignment genuinely never happens
var user = fetchUser();
// And avoid redundant explicit typing when inference is unambiguous
String name = 'Dara'; // the `String` here adds nothing var wouldn't inferDefaulting to final documents intent: anyone reading the code knows immediately that this binding won't change later, without scanning the rest of the function to check. Reserve var for variables that are genuinely reassigned, and reserve explicit type annotations for the cases where inference would be wrong or unclear — a public API's return type, for instance.
Let null safety do its job — don't fight it with !
// Risky: crashes if user.email happens to be null
String contact = user.email!;
// Better: handle the missing case explicitly
String contact = user.email ?? 'No email on file';Every ! you write is a small bet that you're right about something the compiler couldn't verify. Each one is worth a second look — often a ?? fallback or an if check expresses the same logic without the risk of a runtime crash if the assumption turns out to be wrong.
Keep classes focused, and fields private by default
class Order {
final List<_OrderLine> _lines = [];
void addLine(String item, int quantity) {
_lines.add(_OrderLine(item, quantity));
}
double get total => _lines.fold(0, (sum, line) => sum + line.subtotal);
}Expose only what callers actually need (addLine, total), and keep implementation details (_lines, the internal _OrderLine type) private with a leading underscore. This isn't bureaucracy — it means you can freely change how Order computes its total internally later without breaking any code that depends on it.
Use the tools that ship with the SDK
dart format . # consistent style, no debates about it
dart analyze # catches type errors and lints before you run anything
dart test # runs your test suiteRun dart format before every commit and dart analyze constantly while you work — both are already installed with the SDK, cost nothing, and catch entire categories of mistakes (unused imports, unreachable code, missing awaits) before they become bugs you have to debug at runtime instead.
Write doc comments on anything public
/// Calculates the total price including tax.
///
/// [price] is the pre-tax amount. [taxRate] defaults to 10%.
double calculateTotal(double price, {double taxRate = 0.1}) {
return price + (price * taxRate);
}A /// comment on every public function, class, and non-obvious parameter pays for itself the first time someone (including you, months later) has to use your code without re-reading its implementation.
Where to go from here
The language itself is now familiar territory. The natural next step is Flutter, which builds its entire UI model — widgets, state, layout — directly on top of the classes, null safety, and async patterns you've just learned. Nothing in Flutter will feel foreign; it's the same Dart, applied to building interfaces instead of command-line programs.