Flutter Clean Architecture: Feature-First with BLoC for Scalable, Maintainable Apps
Struggling with messy Flutter codebases? Adopt Clean Architecture organized by features using BLoC for clean separation, easy testing, and effortless scaling in your mobile apps.
The Challenge of Scaling Flutter Apps
Building Flutter apps starts fun—quick prototypes, beautiful UIs. But as features pile up, chaos ensues: tangled dependencies, hard-to-test business logic, and code that's a nightmare to maintain. You've probably faced it: screens buried in deep folders, data sources mixed with UI, and state management that's more headache than help. The result? Slower development, frustrated teams, and apps that crumble under growth.
Enter Clean Architecture with a feature-first twist and BLoC for state management. This approach flips the script: organize by features, not layers, enforce strict boundaries, and handle state declaratively. The outcome? Modular, testable code that scales like a dream, letting you ship faster and refactor fearlessly.
We'll dive deep into this structure, drawn from proven templates like this GitHub repo. By the end, you'll have actionable steps to implement it in your projects.
Why Clean Architecture? Solving Core Problems
Clean Architecture, popularized by Uncle Bob, puts business logic at the center, independent of frameworks, UI, or external devices. In Flutter terms:
- Domain layer: Pure business rules, entities, use cases—no Flutter or platform specifics.
- Data layer: Repositories, data sources (API, local DB), models.
- Presentation layer: UI, BLoCs/Cubits, state handling.
Traditional layer-first organization (lib/data/domain/presentation) works for small apps but falters in large ones—features get scattered across folders. Feature-first groups everything by feature under features/, keeping related code together. Perfect for teams where multiple devs tackle different parts simultaneously.
Outcome:
- Independent features deployable as modules.
- Easier onboarding—new devs grasp one feature at a time.
- Battle-tested for enterprise apps.
Add BLoC (Business Logic Component): Reactive state management with streams. Events in, states out. No more setState spaghetti.
Folder Structure: Your Blueprint for Success
Here's the heart of it. Root-level lib/ splits into core/ (shared utilities) and features/ (modular features). Each feature mirrors Clean Architecture layers.
lib/
├── core/
│ ├── error/
│ │ └── failures.dart
│ ├── network/
│ │ └── network_info.dart
│ └── usecases/
│ └── usecase.dart
├── features/
│ └── feature_name/ # e.g., authentication, todo_list
│ ├── data/
│ │ ├── datasources/
│ │ ├── models/
│ │ └── repositories/
│ ├── domain/
│ │ ├── entities/
│ │ ├── repositories/
│ │ └── usecases/
│ └── presentation/
│ ├── bloc/
│ ├── pages/
│ └── widgets/
└── injection_container.dart # GetIt for DI
Core breakdown:
error/failures.dart: Custom failures (ServerFailure, CacheFailure) for error handling withdartzEither<Failure, Success>.usecases/usecase.dart: Base class for all use cases.
Feature deep dive (e.g., number_trivia feature—a simple example for fetching trivia):
- Domain:
NumberTriviaEntity,NumberTriviaRepository(abstract),GetTriviause case. - Data:
NumberTriviaModel(fromJson),NumberTriviaRemoteDataSource, concreteNumberTriviaRepositoryImpl. - Presentation:
NumberTriviaBlocwith events (GetTriviaEvent) and states (Empty, Loading, Loaded, Error).
This setup ensures data flows one-way: Presentation → Domain → Data → External.
Key Dependencies: Tools That Make It Tick
Lock in reliability with these pubspec.yaml gems:
dependencies:
flutter_bloc: ^8.1.3
dartz: ^0.10.1
equatable: ^2.0.5
get_it: ^7.6.4
connectivity_plus: ^5.0.1
http: ^1.1.0
shared_preferences: ^2.2.2
flutter:
sdk: flutter
flutter_bloc: Core state management.dartz: Functional Either for success/failure.equatable: Easy state comparisons.get_it: Dependency injection—register repos, data sources ininjection_container.dart.
Real-world tip: Use flutter pub deps to visualize your graph and avoid bloat.
Getting Started: Step-by-Step Setup
-
Clone and Run: Grab the template:
git clone https://github.com/SimformSolutionsPvtLtd/flutter_clean_architecture_feature_first.gitflutter pub get flutter pub run build_runner build # For code gen if using freezed flutter run -
Dependency Injection Setup: In
injection_container.dart:final sl = GetIt.instance; void init() { sl.registerLazySingleton<NetworkInfo>(() => ...); sl.registerLazySingleton(() => NumberTriviaRepositoryImpl(...)); // Domain sl.registerLazySingleton<NumberTriviaRepository>(() => sl<NumberTriviaRepositoryImpl>()); }Call
init()inmain.dart. -
BLoC in Action: Example from
number_trivia.// Event abstract class NumberTriviaEvent extends Equatable {} class GetTrivia extends NumberTriviaEvent { final String number; ... } // Bloc class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> { final GetTrivia useCase; NumberTriviaBloc({required this.useCase}) : super(Empty()) { on<GetTrivia>((event, emit) async { emit(Loading()); final failureOrTrivia = await useCase(event.number); emit(failureOrTrivia.fold( (failure) => Error(message: _mapFailureToMessage(failure)), (trivia) => Loaded(trivia: trivia), )); }); } }UI listens via
BlocBuilder.
Outcome: Hot reload works seamlessly; inject mocks for testing.
Adding a New Feature: Hands-On Guide
Scale by example. New weather feature?
- Create
features/weather/with data/domain/presentation subfolders. - Define
WeatherEntity,GetWeatheruse case. - Implement data sources (e.g., WeatherRemoteDataSource with http).
- Wire BLoC: Events, states, inject use case.
- Add screen in
presentation/pages/usingBlocProvider. - Register DI: Data source → Repo → Use case.
- Route it in
app_router.dart.
Pro tip: Use very_good_cli or flutter_gen for boilerplate. Add value—feature-first shines in monorepos; extract to packages later.
Testing: Bulletproof Your Code
TDD-friendly:
- Unit: Mock repos in bloc_test.
blocTest<NumberTriviaBloc>( 'should emit [Loading, Loaded] when data is gotten successfully', build: () => bloc..add(GetTrivia('1')), expect: () => [isA<Loading>(), isA<Loaded>()], ); - Widget: golden tests for UI.
- Integration: flutter_driver.
Coverage? Aim 80%+ on domain/data.
Real-World Applications and Enhancements
- Enterprise Apps: E-commerce with auth, cart features isolated.
- Performance: Lazy-load features with
deferredimports. - Extensions: Add Freezed for models, Riverpod if BLoC feels heavy, Firebase for data.
Common Pitfalls Avoided:
- No UI in domain.
- Repos abstract—swap remote/local.
- Connectivity checks via
NetworkInfo.
This isn't theory—teams at Simform use it for production apps handling millions of users.
Wrapping Up: Transform Your Flutter Workflow
Switch to feature-first Clean Architecture with BLoC, and watch productivity soar. Cleaner code, happier teams, robust apps. Fork the repo, tweak for your needs, and build something epic.
Questions? Dive into the code—it's your new starting point.
<div style="text-align: center; margin-top: 2rem;"> <a href="https://cursor.directory/flutter-clean-architecture-feature-first-bloc" target="_blank" rel="noopener noreferrer" class="view-full-resource-btn" style="display: inline-block; background-color: #f97316; color: white; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600; transition: background-color 0.2s;">View Full Resource</a> </div>Comments
More Blog
View allBuilding Voice Agents with Claude API and ElevenLabs: Conversational AI Guide
Build natural voice agents combining Claude API's superior reasoning with ElevenLabs' lifelike TTS. This end-to-end guide creates a conversational web app with STT, AI chat, and speech synthesis.
Claude vs Mistral Large 2: 2025 Data Analysis Benchmarks and Use Cases
As data volumes explode in 2025, choosing between Claude's reasoning depth and Mistral Large 2's efficiency is critical. We benchmark SQL generation, visualizations, and large datasets to reveal the w
Claude Enterprise for Cybersecurity: Threat Modeling and Incident Response
In the high-stakes world of cybersecurity, rapid threat modeling and incident response can mean the difference between containment and catastrophe. Discover how Claude Enterprise empowers security tea
Claude Code in VS Code: Custom Commands for Refactoring Large Codebases
Refactoring sprawling codebases manually? Harness Claude Code's power in VS Code with custom commands to automate AI-driven refactors across TypeScript and Python projects—saving hours of drudgery.
Claude SDK Rust for Blockchain: Smart Contract Auditing Agents
Build blazing-fast smart contract auditing agents in Rust using the Claude SDK. Harness Claude's reasoning to scan Solidity code for vulnerabilities like reentrancy and overflows.
Advanced Claude Artifacts: Collaborative Editing in Multi-User Sessions
Elevate team productivity with Claude Artifacts in multi-user projects—enable real-time iterative editing for code reviews and docs without leaving the interface.