effective-dart
Instructions for writing Dart and Flutter code following the official recommendations.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Instructions for writing Dart and Flutter code following the official recommendations.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Guidelines for using modern Dart features (v3.0+) such as Records, Pattern Matching, Switch Expressions, Extension Types, Class Modifiers, Wildcards, Null-Aware Elements, and Dot Shorthands.
Core concepts and best practices for `package:test`. Covers `test`, `group`, lifecycle methods (`setUp`, `tearDown`), and configuration (`dart_test.yaml`).
| name | effective-dart |
| description | Instructions for writing Dart and Flutter code following the official recommendations. |
| applyTo | **/*.dart |
Best practices recommended by the Dart and Flutter teams. These instructions were taken from Effective Dart and Architecture Recommendations.
Over the past several years, we've written a ton of Dart code and learned a lot about what works well and what doesn't. We're sharing this with you so you can write consistent, robust, fast code too. There are two overarching themes:
Be consistent. When it comes to things like formatting, and casing, arguments about which is better are subjective and impossible to resolve. What we do know is that being consistent is objectively helpful.
If two pieces of code look different it should be because they are different in some meaningful way. When a bit of code stands out and catches your eye, it should do so for a useful reason.
Be brief. Dart was designed to be familiar, so it inherits many of the same statements and expressions as C, Java, JavaScript and other languages. But we created Dart because there is a lot of room to improve on what those languages offer. We added a bunch of features, from string interpolation to initializing formals, to help you express your intent more simply and easily.
If there are multiple ways to say something, you should generally pick the most concise one. This is not to say you should code golf yourself into cramming a whole program into a single line. The goal is code that is economical, not dense.
We split the guidelines into a few separate topics for easy digestion:
Style – This defines the rules for laying out and organizing code, or at least the parts that dart format doesn't handle for you. The style topic also specifies how identifiers are formatted: camelCase, using_underscores, etc.
Documentation – This tells you everything you need to know about what goes inside comments. Both doc comments and regular, run-of-the-mill code comments.
Usage – This teaches you how to make the best use of language features to implement behavior. If it's in a statement or expression, it's covered here.
Design – This is the softest topic, but the one with the widest scope. It covers what we've learned about designing consistent, usable APIs for libraries. If it's in a type signature or declaration, this goes over it.
Each topic is broken into a few sections. Sections contain a list of guidelines. Each guideline starts with one of these words:
DO guidelines describe practices that should always be followed. There will almost never be a valid reason to stray from them.
DON'T guidelines are the converse: things that are almost never a good idea. Hopefully, we don't have as many of these as other languages do because we have less historical baggage.
PREFER guidelines are practices that you should follow. However, there may be circumstances where it makes sense to do otherwise. Just make sure you understand the full implications of ignoring the guideline when you do.
AVOID guidelines are the dual to "prefer": stuff you shouldn't do but where there may be good reasons to on rare occasions.
CONSIDER guidelines are practices that you might or might not want to follow, depending on circumstances, precedents, and your own preference.
Some guidelines describe an exception where the rule does not apply. When listed, the exceptions may not be exhaustive—you might still need to use your judgement on other cases.
This sounds like the police are going to beat down your door if you don't have your laces tied correctly. Things aren't that bad. Most of the guidelines here are common sense and we're all reasonable people. The goal, as always, is nice, readable and maintainable code.
UpperCamelCase.UpperCamelCase.lowercase_with_underscores.lowercase_with_underscores.lowerCamelCase.lowerCamelCase for constant names.dart: imports before other imports.package: imports before relative imports.dart format./// doc comments to document members and types.part of directives.src directory of another package.lib.null.null.true or false in equality operations.late variables if you need to check whether they are initialized..length to see if a collection is empty.Iterable.forEach() with a function literal.List.from() unless you intend to change the type of the result.whereType() to filter a collection by type.cast() when a nearby operation will do.cast().var and final on local variables.final field to make a read-only property.=> for simple members.this. except to redirect to a named constructor or to avoid shadowing.late when a constructor initializer list will do.; instead of {} for empty constructor bodies.new.const redundantly.on clauses.on clauses.Error only for programmatic errors.Error or types that implement it.rethrow to rethrow a caught exception.async when it has no useful effect.Future<T> when disambiguating a FutureOr<T> whose type argument could be Object.get.to...() if it copies the object's state to a new object.as...() if it returns a different representation backed by the original object.mixin or pure class to a mixin class.const if the class supports it.final.late final fields without initializers.Future, Stream, and collection types.this from methods just to enable a fluent interface.dynamic instead of letting inference fail.dynamic unless you want to disable static checking.Future<void> as the return type of asynchronous members that do not produce values.FutureOr<T> as a return type.hashCode if you override ==.== operator obey the mathematical rules of equality.== nullable.This page presents architecture best practices, why they matter, and whether we recommend them for your Flutter application. You should treat these recommendations as recommendations, and not steadfast rules, and you should adapt them to your app's unique requirements.
The best practices on this page have a priority, which reflects how strongly the Flutter team recommends it.
You should separate your app into a UI layer and a data layer. Within those layers, you should further separate logic into classes by responsibility.
Strongly recommend
Separation of concerns is the most important architectural principle. The data layer exposes application data to the rest of the app, and contains most of the business logic in your application. The UI layer displays application data and listens for user events from users. The UI layer contains separate classes for UI logic and widgets.
Strongly recommend
The repository pattern is a software design pattern that isolates the data access logic from the rest of the application. It creates an abstraction layer between the application's business logic and the underlying data storage mechanisms (databases, APIs, file systems, etc.). In practice, this means creating Repository classes and Service classes.
Strongly recommend
Separation of concerns is the most important architectural principle. This particular separation makes your code much less error prone because your widgets remain "dumb".
ChangeNotifiers and Listenables to handle widget updates.Conditional
There are many options to handle state-management, and ultimately the decision comes down to personal preference.
The ChangeNotifier API is part of the Flutter SDK, and is a convenient way to have your widgets observe changes in your ViewModels.
Strongly recommend
Logic should be encapsulated in methods on the ViewModel. The only logic a view should contain is:
Conditional
Use in apps with complex logic requirements.
A domain layer is only needed if your application has exceeding complex logic that crowds your ViewModels, or if you find yourself repeating logic in ViewModels. In very large apps, use-cases are useful, but in most apps they add unnecessary overhead.
Handling data with care makes your code easier to understand, less error prone, and prevents malformed or unexpected data from being created.
Strongly recommend
Data updates should only flow from the data layer to the UI layer. Interactions in the UI layer are sent to the data layer where they're processed.
Commands to handle events from user interaction.Recommend
Commands prevent rendering errors in your app, and standardize how the UI layer sends events to the data layer.
Strongly recommend
Immutable data is crucial in ensuring that any necessary changes occur only in the proper place, usually the data or domain layer. Because immutable objects can't be modified after creation, you must create a new instance to reflect changes. This process prevents accidental updates in the UI layer and supports a clear, unidirectional data flow.
Recommend
You can use packages to help generate useful functionality in your data models, freezed or built_value.
These can generate common model methods like JSON ser/des, deep equality checking and copy methods.
These code generation packages can add significant build time to your applications if you have a lot of models.
Conditional
Use in large apps.
Using separate models adds verbosity, but prevents complexity in ViewModels and use-cases.
Well organized code benefits both the health of the app itself, and the team working on the code.
Strongly recommend
Dependency injection prevents your app from having globally accessible objects, which makes your code less error prone.
We recommend you use the provider package to handle dependency injection.
Recommend
Go_router is the preferred way to write 90% of Flutter applications.
auto_route is also an accepted alternative, particularly useful when you need strongly-typed route parameters or prefer a code-generation based approach.
There are some specific use-cases that go_router or auto_route doesn't solve,
in which case you can use the Flutter Navigator API directly or try other packages found on pub.dev.
Recommend
We recommend naming classes for the architectural component they represent. For example, you may have the following classes:
For clarity, we do not recommend using names that can be confused with objects from the Flutter SDK.
For example, you should put your shared widgets in a directory called ui/core/,
rather than a directory called /widgets.
Strongly recommend
Repository classes are the sources of truth for all data in your app, and facilitate communication with external APIs. Creating abstract repository classes allows you to create different implementations, which can be used for different app environments, such as "development" and "staging".
Good testing practices makes your app flexible. It also makes it straightforward and low risk to add new logic and new UI.
Strongly recommend
Strongly recommend
Fakes aren't concerned with the inner workings of any given method as much as they're concerned with inputs and outputs. If you have this in mind while writing application code, you're forced to write modular, lightweight functions and classes with well defined inputs and outputs.