match_lib_folder_structure
Flags a file under lib/ sitting in a folder whose name is not lower_snake_case.
A folder name becomes part of every package: URI that imports through it, so it is public API in a way a local variable name is not — renaming it later is a breaking change for every consumer.
CamelCase and kebab-case folders also break on case-insensitive filesystems. A folder renamed from Models to models is invisible to git on macOS by default, so the import keeps resolving on the machine that made the change and fails in CI.
The SDK’s file_names rule checks the file; nothing in the SDK checks the directories above it, which is the gap this fills.
This rule is in the pedantic preset. It works out of the box; enable it in any other preset by name:
rules: match_lib_folder_structure: enabled: trueSee also: file_names, Effective Dart: naming
lib/dataSources/user_repository.dartlib/data-sources/user_repository.dartlib/Models/user.dartlib/data_sources/user_repository.dartlib/models/user.dartExamples
Section titled “Examples”The suggestion converts the name for you
Section titled “The suggestion converts the name for you”The diagnostic names both the offending folder and its lower_snake_case form, so the rename is spelled out:
lib/dataSources/user.dart → data_sourceslib/data-sources/user.dart → data_sourceslib/UserProfile/view.dart → user_profileNested folders are all checked, but reported once per file
Section titled “Nested folders are all checked, but reported once per file”Every folder between the root and the file is examined. Only the first offender reports, since a second diagnostic on the same path would be fixed by the same kind of rename:
lib/Features/userProfile/view.dartReported once, at Features. Fix it and re-analyze to see userProfile.
Digits and underscores are fine
Section titled “Digits and underscores are fine”The check is ^[a-z0-9]+(_[a-z0-9]+)*$, so a folder may contain digits and underscore-separated words — it just may not start or end with an underscore, or double one up:
lib/api_v2/client.dart ✓lib/oauth2/token.dart ✓lib/_internal/helpers.dart ✗ leading underscorelib/data__sources/user.dart ✗ doubled underscorePointing the rule at a different root
Section titled “Pointing the rule at a different root”root: moves the whole check. Set it when your package’s sources are not under lib/:
many_lints: rules: match_lib_folder_structure: root: packagesrules: match_lib_folder_structure: root: packagesFiles outside packages/ are then never reported — including everything under lib/.
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
root |
string | lib |
The top-level directory the rule applies to. Files outside it are never reported |
Known limitations
Section titled “Known limitations”A file directly in the root has no folders to check. lib/main.dart is never reported; the rule looks only at the segments between the root and the file name.
The root itself is never checked, only what is below it — root: lib does not report lib for anything.
The file name is not this rule’s job. file_names covers that, and prefer_match_file_name covers whether the name describes the contents.
No quick fix. Renaming a folder moves files and rewrites every import that passes through it, which is a refactoring your IDE or git mv should drive, not a single-file edit.
Turning this rule off
Section titled “Turning this rule off”To disable this rule:
rules: match_lib_folder_structure: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”prefer_match_file_name— Name a file after the first public declaration in it.prefer_single_declaration_per_file— Keep one top-level declaration per file, with per-type budgets.prefer_single_widget_per_file— Keep one public widget per file for better organization.arguments_ordering— Keep named arguments in a configured order.