prefer_single_declaration_per_file
This rule warns when a file declares more than one top-level declaration. Classes, mixins, enums, extensions and extension types are counted; the first is fine, and the second and any subsequent ones report. Private declarations are skipped by default.
Through the groups: option the same rule also covers the type-specific conventions — one bloc per file, one notifier per file — with each group carrying its own budget, so a file holding one of each satisfies both.
Why use this rule
Section titled “Why use this rule”A file holding several unrelated declarations is harder to navigate, harder to review, and makes the file system a poor index of the codebase. Splitting them keeps imports honest — a file pulls in exactly the declaration it needs — and makes each type independently testable.
The type-scoped form matters most for state management, where a file holding two blocs or two notifiers usually means two features have quietly grown together.
See also: Effective Dart: libraries, Flutter architecture recommendations
// user.dart — three unrelated declarations in one fileclass User {}
// This triggers the lintclass UserRepository {}
// So does thisenum UserRole { admin, guest }// user.dart — one declaration per fileclass User {}
// Private helpers stay put: they are invisible outside the file,// so they cannot be what forces a reader to open it.class _UserCache {}class UserRepository {}Turning this rule off
Section titled “Turning this rule off”This rule is in the pedantic preset — it imposes a file-organization
convention, so it runs with that preset or when a project enables it by name:
rules: prefer_single_declaration_per_file: trueTo turn it off again:
rules: prefer_single_declaration_per_file: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Options
Section titled “Options”many_lints: rules: prefer_single_declaration_per_file: kinds: [class, mixin] ignore_private: falserules: prefer_single_declaration_per_file: kinds: [class, mixin] ignore_private: false| Option | Type | Default | Description |
|---|---|---|---|
kinds |
list | all five kinds | Which declaration kinds count: class, mixin, enum, extension, extension_type |
types |
list | (empty) | Narrow to subtypes of these base types. Empty counts every declaration of the configured kinds |
ignore_private |
bool | true |
Skip declarations whose name starts with _ |
ignore_visible_for_testing |
bool | false |
Skip declarations annotated @visibleForTesting |
groups |
list | (one implicit group) | Independently counted groups; see below |
Narrowing to one type
Section titled “Narrowing to one type”types: turns the rule into the type-specific convention. Matching walks the
whole supertype hierarchy, so an indirect subclass counts too:
many_lints: rules: prefer_single_declaration_per_file: types: [Notifier, AsyncNotifier]rules: prefer_single_declaration_per_file: types: [Notifier, AsyncNotifier]With this, a file may hold as many plain helper classes as it likes, but only one notifier.
Independent budgets with groups
Section titled “Independent budgets with groups”Each group in groups: is counted separately, so several conventions coexist
without interfering. A file holding one bloc and one notifier satisfies
both budgets — folding them into a single count would report exactly the layout
the project asked for.
many_lints: rules: prefer_single_declaration_per_file: groups: - types: [Bloc, Cubit] message: 'One bloc per file.' - types: [Notifier, AsyncNotifier] message: 'One notifier per file.'rules: prefer_single_declaration_per_file: groups: - types: [Bloc, Cubit] message: 'One bloc per file.' - types: [Notifier, AsyncNotifier] message: 'One notifier per file.'Each group accepts kinds, types, ignore_private,
ignore_visible_for_testing and its own message. Any of those written at the
top level become the groups’ defaults, so a shared setting is stated once
rather than repeated:
rules: prefer_single_declaration_per_file: ignore_private: false # applies to both groups below groups: - types: [Bloc] - types: [Notifier] ignore_private: true # ...unless a group overrides itA declaration matching several groups is counted by the first matching group only, so overlapping groups never report the same declaration twice.
Related rules
Section titled “Related rules”prefer_single_widget_per_file— Keep one public widget per file for better organization.prefer_match_file_name— Name a file after the first public declaration in it.match_lib_folder_structure— Keep folders under lib/ in lower_snake_case.arguments_ordering— Keep named arguments in a configured order.