clean_warden_flutter 1.0.1
clean_warden_flutter: ^1.0.1 copied to clipboard
A strict, powerful, and automated architectural boundary enforcement tool for Flutter projects. It intercepts runtime data flows to prevent Clean Architecture violations.
Clean Warden Flutter #
A strict, powerful, and automated architectural boundary enforcement tool for Flutter projects utilizing Clean Architecture.
This package acts as an automated runtime "Warden." It deeply scans data flows moving through your state management tools and instantly flags architectural violations, ensuring that your Presentation layer never depends on Data models, and your Domain layer remains free of external dependencies.
Why This Package is Important #
As a project scales, architectural erosion is inevitable. Junior developers or rushed timelines often lead to shortcuts, such as passing a UserModel (Data Layer) directly to a Widget (Presentation Layer) or processing a NetworkResponse directly inside a Use Case or Entity (Domain Layer).
Simple code reviews are not scalable for catching every leak. Clean Warden automates this supervision. By intercepting data flows at runtime, the package creates a fail-safe that loudly alerts developers the exact moment an architectural boundary is breached. It acts as an automated Senior Architect sitting alongside every developer on your team.
Architectural Principles #
This package enforces Dependency Inversion and the Interface Segregation Principle. It treats the 'Domain' as the center of the universe, and 'Data' and 'Presentation' as detail-oriented plugins that should never talk directly.
Core Features #
- Strict Boundary Rules: Automatically triggers violations if structural boundaries are broken.
- State Management Native: Seamlessly ties into
flutter_blocandflutter_riverpodrespectively. - In-App SnackBar Alerter: Optional floating UI alerts that display violations instantly on-screen without requiring the terminal.
- High-Visibility Formatting: Terminal warnings utilize ANSI color codes with highly visible separation borders alongside useful "Suggested Fixes".
- Sensitive Data Masking: Hardcoded protections mask attributes like
password,token, andnifout of the raw error payloads to secure your users' information in the logs.
Performance & Compatibility #
Because we avoided dart:mirrors in favor of string-based type checking, the package adds zero overhead to the Flutter build and is 100% compatible with AOT compilation and Flutter Web.
Installation #
Add the package to your pubspec.yaml:
dependencies:
clean_warden_flutter: ^1.0.0
Getting Started #
1. The Identity System (WardenMember) #
For the Warden to track your architecture, your state management classes need to declare which layer they belong to. You do this by mixing in WardenMember.
import 'package:clean_warden_flutter/clean_warden_flutter.dart';
// By mixing in WardenMember, we tell the engine this is a UI component.
class ProfileBloc extends Bloc<ProfileEvent, ProfileState> with WardenMember {
@override
WardenLayer get layer => WardenLayer.presentation;
ProfileBloc() : super(ProfileInitial()) {
on<LoadProfile>((event, emit) {
// Logic goes here
});
}
}
Available tracking layers include:
WardenLayer.presentationWardenLayer.domainWardenLayer.dataWardenLayer.infrastructure
2. Configuration and Observers #
Launch the engine in your main.dart file and configure it to your strictness preference. You must also supply the observers to your respective state management tool.
For BLoC:
import 'package:clean_warden_flutter/clean_warden_flutter.dart';
void main() {
// Configure the engine
WardenConfig.setup(const WardenConfig(
mode: LogMode.logOnly, // Options: logOnly, strictCrash
enableInAppAlerts: true,
));
// Connect the observer seamlessly to BLoC
Bloc.observer = WardenBlocObserver();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
// Pass the messenger key so SnackBar alerts can trigger globally
scaffoldMessengerKey: WardenConfig.messengerKey,
home: const HomeScreen(),
);
}
}
For Riverpod:
void main() {
WardenConfig.setup(const WardenConfig(mode: LogMode.strictCrash));
runApp(
ProviderScope(
observers: [WardenProviderObserver()],
child: const MyApp(),
)
);
}
How it Works & What Errors Look Like #
Under the hood, clean_warden_flutter utilizes strict runtimeType.toString() checks on the states emitted by your application.
Rule 1: The Presentation Leak #
Your Presentation Layer (Widgets, UI State) should only interact with Domain Entities or local UI Models. It should never interact with Data Models or Data Transfer Objects (DTOs).
If a class marked with WardenLayer.presentation emits an object containing the word "Model" or "Dto", the Warden intercepts it.
If a developer writes:
emit(UserModel(id: 1, name: "John"));
The user will see the following terminal error:
=========================================
ARCHITECTURAL VIOLATION DETECTED!
Location: ProfileBloc (Layer: presentation)
Offending Data: UserModel
Rule Broken: Presentation Logic should interact with Domain Entities, not Data Models.
Suggested Fix: Did you forget to use a Mapper in the Domain Layer? Map your Data Models to Domain Entities or Presentation UI Models before passing them to the UI.
=========================================
Rule 2: The Domain Leak #
Your Domain core is the heart of Clean Architecture and must not depend on anything. It should never process HTTP Requests or API Responses directly.
If a class marked with WardenLayer.domain works with an object containing "Response", "Request", "Model", or "Dto", the Warden intercepts it.
The user will see the following terminal error:
=========================================
ARCHITECTURAL VIOLATION DETECTED!
Location: FetchUserUseCase (Layer: domain)
Offending Data: LoginResponse
Rule Broken: Domain layer must be completely independent of Data representations (Models/Responses).
Suggested Fix: Domain layer should only communicate via abstractions (Contracts/Repositories) and Entities. Move the Reponses and Models out to the Data layer and map them.
=========================================
Advanced Configurations #
Safe Logging vs Strict Enforcements #
Through WardenConfig, the architecture's strictness can be toggled via LogMode.
LogMode.logOnly: Recommended for initial adoption or production environments. The engine cleanly formats the violation in the terminal or SnackBar but allows the code to execute normally without interrupting the user.LogMode.strictCrash: Recommended for development and QA builds. If an architectural violation occurs, an unhandledWardenViolationExceptionis immediately thrown, intentionally crashing the app to force developers to fix the mapping layer before committing their code.
Gradual Adoption for Legacy Apps #
Applying strict architecture rules to a massive, 3-year-old codebase can suddenly trigger hundreds of errors. Clean Warden is purposely designed for gradual adoption!
You can restrict the Warden to only monitor specific features, or temporarily ignore problematic layers via your WardenConfig:
WardenConfig.setup(const WardenConfig(
mode: LogMode.strictCrash,
// Only monitor the new "checkout" and "auth" features
allowedFeatures: ['checkout', 'auth'],
// Temporarily ignore the infrastructure layer while it's being refactored
ignoredLayers: [WardenLayer.infrastructure],
));
To bind a class to a feature, override the optional featureName inside your WardenMember:
class CheckoutBloc extends Bloc<dynamic, dynamic> with WardenMember {
@override
WardenLayer get layer => WardenLayer.presentation;
@override
String get featureName => 'checkout'; // The Warden is watching this feature!
}
Custom State Management (Provider, GetX, MobX) #
If you aren't using BLoC or Riverpod, you can still easily utilize the system by placing the manual verification check inside your setters or notifyListeners().
class AuthViewModel extends ChangeNotifier with WardenMember {
@override
WardenLayer get layer => WardenLayer.presentation;
void updateState(Object newState) {
// Manually run the verification check before applying the state
WardenEngine.check(this, newState);
// ... apply state & pass to UI
}
}
In-App Alerter #
If enableInAppAlerts: true is passed to the configuration and the WardenConfig.messengerKey is attached to your MaterialApp, a prominent UI SnackBar will drop down from the top of the user's screen reading:
Clean Warden Alert: PRESENTATION improperly received UserModel
This enables developers and QA testers to physically see the architectural leaks directly on a test device without monitoring the terminal.
Sensitive Data Masking #
Error logs inherently print string representations of objects. If an object payload contains security keys, Clean Warden intercepts the string and automatically masks it.
If a rejected payload has password: "secretCode123" inside its parameters, the console output will scrub it to read: password: ***. Supported automatic masked keys include: password, token, auth, secret.
Community & Contributing #
This package is maintained as a contribution to the global Flutter ecosystem. Developers are encouraged to submit pull requests and resolve issues through the official repository.
License #
This project is licensed under the MIT License - see the LICENSE file for details.
Meet the Author #
Developed and maintained by Muhammad Omar.
- LinkedIn: muhammad-omar-0335
- GitHub: umarKhan1