clean_arch_lint
Custom lint for Flutter Clean Architecture, focused on enforcing layers using static analysis (AST) with custom_lint.
This package acts as an architecture guardian: if a layer depends on something it shouldn't, the error shows up immediately.
🎯 Objective
Ensure the structure below is automatically respected:
lib/
├─ core/
├─ data/
└─ presentation/
No PR discussions. No "it was unintentional". The lint solves it.
🧱 Layer Concepts
core
Pure layer, without Flutter and without infrastructure.
Contains:
- entities
- usecases
- contracts (interfaces)
- business rules
data
Technical implementations.
Contains:
- datasources
- models / DTOs
- mappers
- repository implementations (
Impl)
presentation
User interface.
Contains:
- widgets
- pages
- bloc / cubit
- viewmodels / controllers
🚨 Lint Rules
1️⃣ core_no_flutter (ERROR)
❌ Prohibits Flutter imports in core.
Blocks:
package:flutter/*dart:uipackage:flutter_test/*
Reason: Core must be completely independent of UI.
2️⃣ core_no_data_or_presentation (ERROR)
❌ Prohibits core from depending on data or presentation.
Clean Architecture golden rule:
Dependencies always point inward.
3️⃣ data_no_presentation (ERROR)
❌ data cannot import anything from presentation.
Reason:
- Avoids coupling infrastructure with UI
- Ensures testability
4️⃣ presentation_no_data (WARNING configurable)
⚠️ By default, presentation should not depend directly on data.
✔️ Usecases and contracts should come from core.
This rule can be configured to ERROR.
📦 Installation
1) Add dependencies to your Flutter app
dev_dependencies:
custom_lint: ^0.8.1
clean_arch_lint:
path: ../clean_arch_lint
# Or, when published:
# clean_arch_lint: ^1.0.0
Adjust the
pathaccording to your repository structure.
2) Enable the plugin in analysis_options.yaml
analyzer:
plugins:
- custom_lint
▶️ How to Run
# Single execution
dart run custom_lint
# Watch mode (re-executes when saving files)
dart run custom_lint --watch
In VSCode / Android Studio:
- Errors appear automatically in the editor
- Works in real-time as you type
⚙️ Configuration
Make presentation_no_data an ERROR
custom_lint:
rules:
- presentation_no_data:
severity: error
Ignore specific paths (example)
custom_lint:
rules:
- core_no_flutter:
ignore:
- lib/core/di/**
Useful for very specific cases like DI bootstrap.
✅ Examples
Allowed import
import 'package:my_app/core/usecases/get_user.dart';
Prohibited import (core → flutter)
import 'package:flutter/material.dart'; // ❌ error
Prohibited import (presentation → data)
import 'package:my_app/data/user_repository_impl.dart'; // ⚠️ or ❌
🧠 Recommended Best Practices
- Interfaces always in
core - Implementations always in
data - UI depends only on abstractions
- Dependency injection resolves the rest
❌ What This Lint Does NOT Do
- Does not generate code
- Does not automatically fix
- Does not replace code review
It only points out the error before it becomes technical debt.
🧩 Technical Stack
- Dart SDK >= 3.0
- analyzer
- custom_lint_builder
- path
No build_runner. No source_gen.
🏁 Quick Summary
| Layer | Can depend on |
|---|---|
| core | core only |
| data | core, data |
| presentation | core, presentation |
If it goes beyond that, the lint alerts.
📁 Supported Structures
The lint automatically supports two folder structures:
Structure 1: Direct (simple projects)
lib/
├─ core/
├─ data/
└─ presentation/
Structure 2: With src/ (larger projects)
lib/
└─ src/
├─ core/
├─ data/
└─ presentation/
No additional configuration needed - the lint automatically detects which structure you're using!
Clean architecture is not an opinion. It's a contract.
Libraries
- clean_arch_lint
- Custom lint plugin for enforcing Flutter Clean Architecture rules.