Skip to content

avoid_banned_imports

v1.0.0WarningConfigurableArchitecture

Flags imports of libraries you ban, optionally only inside the directories you name. This is architecture enforcement: it keeps a Clean Architecture domain layer free of Flutter, stops dart:io reaching code that also targets the web, and confines a legacy package to the module still using it.

This rule reports nothing until you configure it. Installing this package never restricts an import on its own.

Layering rules are the easiest architectural decision to make and the hardest to keep. A domain layer stays platform-independent only as long as nobody types import 'package:flutter/material.dart' — and code review catches that in the pull request it appears in, not in the fifty that follow. A lint makes the boundary mechanical, so the constraint outlives the person who set it.

The payoff shows up when it is needed most. A domain layer with no Flutter import can be tested without a widget binding and reused in a CLI or server; one that has drifted takes a refactor to get back.

See also: Clean Architecture: the dependency rule, Flutter’s guide to app architecture

// in lib/domain/user_repository.dart
//
// With an entry banning 'dart:io' in lib/domain/**:
import 'dart:io'; // LINT: the domain layer can no longer target the web
Future<String> readConfig(File file) => file.readAsString();
// in lib/domain/user_repository.dart
//
// Depend on an abstraction the domain layer owns; let the data layer import
// 'dart:io' and implement it.
abstract class ConfigSource {
Future<String> read();
}
class UserRepository {
const UserRepository(this._config);
final ConfigSource _config;
Future<String> load() => _config.read();
}

This rule is in no preset, so it is off unless you enable it by name:

many_lints.yaml
rules:
avoid_banned_imports: true

To turn it off again:

many_lints.yaml
rules:
avoid_banned_imports: false

To keep the rule on but skip certain paths, use per-rule exclude.

analysis_options.yaml
many_lints:
rules:
avoid_banned_imports:
banned:
- deny: ['package:flutter/material.dart']
in: ['lib/domain/**', 'lib/data/**']
message: 'The domain layer must not depend on Flutter.'
- deny_pattern: ['package:legacy_.*']
message: 'Legacy packages are being removed; see ADR-014.'
Option Type Default Description
banned list of maps [] The entries to enforce. With none, the rule reports nothing

Each entry accepts:

Key Type Required Description
deny string or list one of deny / deny_pattern Import URIs banned by exact match
deny_pattern string or list one of deny / deny_pattern Regular expressions, anchored to the whole URI
in list of globs no Paths, relative to the package root, where the entry applies. Omit to apply everywhere
message string no A project-specific explanation appended to the diagnostic

Match the URI exactly as you write it in the import: package:flutter/material.dart, not a file path.

deny is exact by default, so banning async does not also ban dart:async. Opt into patterns explicitly with deny_pattern, which is anchored to the whole URI — dart:.* matches every dart: library, and legacy matches only a library named exactly legacy.

in takes globs, the same syntax as exclude, so path semantics are identical across the whole config file and Windows separators are normalized for you.

An entry denying nothing is skipped, and an invalid regular expression costs you that pattern and nothing else. Because a plugin cannot report problems against a YAML file, malformed configuration degrades quietly rather than failing analysis.