clean_code_lints 0.3.2
clean_code_lints: ^0.3.2 copied to clipboard
Opinionated lint rules for clean architecture and clean code in Dart and Flutter projects.
clean_code_lints #
Opinionated lint rules for clean architecture and clean code in Dart and Flutter projects.
clean_code_lints is built on Dart's first-party analysis_server_plugin system — no custom_lint runner, no separate process. It targets the conventions most "clean architecture" Flutter projects converge on: layered folders, design tokens, small files, no framework leakage into domain code.
Requirements #
- Dart SDK >= 3.10
- Flutter >= 3.38 (if used in a Flutter project)
Install #
Enable the plugin and opt into the rules in your analysis_options.yaml:
plugins:
clean_code_lints:
version: ^0.3.2
diagnostics:
# Size & shape
file_too_long: true
function_too_long: true
class_too_long: true
build_method_too_long: true
too_many_function_arguments: true
# Naming
single_letter_parameter: true
hungarian_notation: true
# Layered architecture
domain_layer_imports: true
presentation_imports_storage: true
feature_isolation: true
# Widget hygiene
widget_level_try_catch: true
widget_build_method: true
# Hardcoded literals
hardcoded_color: true
hardcoded_spacing: true
hardcoded_endpoint: true
# Clean code
no_print_in_production: true
avoid_dynamic: true
missing_await: true
enum_exhaustive_switch: true
After editing plugins:, restart the Dart Analysis Server (IDE command "Dart: Restart Analysis Server" or restart your IDE) — the analyzer only re-reads the plugins: section on restart.
Diagnostics flow through the standard analyzer pipeline, so dart analyze and your IDE's Problems view both surface them.
Do not add
clean_code_lintsto yourpubspec.yaml. Unlike custom_lint-era packages, native analyzer plugins are installed only through theplugins:section above. The analysis server resolves the plugin in its own isolated package context, so itsanalyzerdependency never meets your app's dependencies. Adding it todev_dependenciesinstead pulls thatanalyzerconstraint into your app's resolution, where it will conflict with codegen packages likefreezed,json_serializable, ormockito.
Compatibility #
The plugin supports analyzer >=11.0.0 <15.0.0 (analysis_server_plugin
>=0.3.11 <0.4.0), which covers Dart 3.10 and newer — pub automatically picks
the pairing that matches your SDK. The range is kept deliberately wide because
every plugin listed in an analysis_options.yaml resolves in one shared
context: a plugin pinned to a single analyzer major conflicts with any
neighbour pinned to a different one. Rules are written against AST APIs that
are stable across the whole range (see lib/src/ast_compat.dart), and CI
runs the full test suite against both edges.
Rules #
19 rules, all off by default. Enable them individually in diagnostics:.
Size & shape #
| Rule | Default cap | What it flags |
|---|---|---|
file_too_long |
400 lines | Any .dart file longer than the cap. |
function_too_long |
40 lines | Function or method bodies longer than the cap. |
class_too_long |
300 lines | Class declarations longer than the cap. |
build_method_too_long |
40 lines | Widget build() bodies longer than the cap. |
too_many_function_arguments |
5 params | Functions, methods, or constructors with more parameters than the cap. |
Naming #
| Rule | What it flags |
|---|---|
single_letter_parameter |
Parameter names with a single character (except the discard _). |
hungarian_notation |
Identifiers prefixed with a type abbreviation (strName, boolFoo, etc.). |
Layered architecture #
| Rule | What it flags |
|---|---|
domain_layer_imports |
Files under lib/domain/ importing Flutter, infra packages, or sibling layers. |
presentation_imports_storage |
Files under lib/presentation/ importing storage packages or the data layer. |
feature_isolation |
Files under lib/features/<a>/ importing files under lib/features/<b>/. |
Widget hygiene #
| Rule | What it flags |
|---|---|
widget_level_try_catch |
try/catch inside a class extending Widget / State / ConsumerWidget / HookWidget etc. |
widget_build_method |
Private _buildSomething() methods returning a Widget. Prefer a _Section widget class. |
Hardcoded literals #
| Rule | What it flags |
|---|---|
hardcoded_color |
Color(...) constructors and Colors.X references outside theme / token files. |
hardcoded_spacing |
Numeric literals passed to SizedBox / EdgeInsets / Padding / Gap outside theme / token files. |
hardcoded_endpoint |
String literals beginning with http:// or https:// outside config / endpoint files. |
Clean code #
| Rule | What it flags |
|---|---|
no_print_in_production |
Top-level print(...) calls outside test/, tool/, and bin/ directories. |
avoid_dynamic |
Explicit dynamic type annotations outside test code. |
missing_await |
Future-returning expressions used as statements without await, assignment, return, or unawaited(...). |
enum_exhaustive_switch |
Switch statements on enum types that lack a default: clause and do not cover every constant. |
Conventions assumed by path-based rules #
The rules below use path matching, not configuration. They expect a project layout where the relevant segment is somewhere in the file path:
domain_layer_imports— path contains/domain/.presentation_imports_storage— path contains/presentation/.feature_isolation— path matches/features/<feature_name>/.hardcoded_color— allow-listed paths:/theme/,/colors/,/tokens/,/design_system/.hardcoded_spacing— allow-listed paths:/theme/,/tokens/,/spacing/,/dimensions/,/design_system/.hardcoded_endpoint— allow-listed paths:/api/,/endpoints/,/network/,/config/,/environment/.no_print_in_production— allow-listed paths:/test/,/tool/,/bin/.avoid_dynamic— allow-listed paths:/test/, files ending_test.dart.
These are opinionated v1 defaults. Configurable knobs (custom paths, custom caps, custom forbidden-import lists) are planned — see roadmap.
Local development #
To try the plugin against the bundled example:
cd example
flutter pub get
dart analyze
You should see diagnostics from multiple rules across example/lib/.
Roadmap #
Rules that need richer specs, deferred to a later milestone:
no_logic_in_build— flag side-effectful method calls inbuild()(needs careful spec to avoid false positives).hardcoded_user_string— flag user-visible string literals (must integrate with intl / ARB / slang).hardcoded_storage_key— flag string literals passed to storage APIs (needs target API knowledge).- Configurable variants of the path-based rules (custom layer paths, custom forbidden-import lists, custom size caps).
License #
MIT — see LICENSE.