## Why Choose Clean Architecture in Flutter with Feature-First Organization?
Building large-scale Flutter applications demands a solid foundation. Traditional layered architectures often lead to tangled code, making maintenance a nightmare. Enter Clean Architecture: a methodology that separates concerns into distinct layers—presentation, domain, and data—ensuring your business logic stays pure and testable.
But why feature-first? Instead of grouping by layers (like all repositories in one folder), this approach organizes code by app features (e.g., authentication, user profiles). This keeps related code together, speeding up development and onboarding. Combined with BLoC for reactive state management, it handles complex UIs without boilerplate overload.
Real-world example: Imagine an e-commerce app. Features like 'cart' and 'checkout' stay isolated, so refactoring one doesn't ripple everywhere. Check out the [template repo](https://github.com/FilledStacks/flutter-clean-architecture-feature-first-bloc) for a ready-to-use starter.
## What Does the Project Structure Look Like?
Dive into the folder layout:
- **lib/src/core/**: App-wide utilities. Houses error handling, network configs, and themes. Keeps globals isolated.
- **lib/src/shared/**: Reusable components crossing features, like custom widgets or data models.
- **lib/src/features/**: Heart of the app. Each feature (e.g., `posts`) has subfolders:
- `presentation/`: BLoC, pages, widgets.
- `domain/`: Entities, use cases, repositories (abstract).
- `data/`: Models, datasources, repositories (impl).
```
lib/
src/
core/
error/
network/
shared/
features/
posts/
presentation/
domain/
data/
```
This mirrors Uncle Bob's Clean Architecture: outer layers depend on inner ones via dependency inversion. Domain layer is king—no external deps like Flutter or Dio.
## Key Dependencies and Setup
The template pulls in essentials without bloat:
- **flutter_bloc & bloc**: Reactive state via Cubit or BLoC.
- **equatable**: Hashable entities for efficient comparisons.
- **dartz**: Functional Either for error handling.
- **freezed**: Code-gen for models (union types, serialization).
- **injectable & get_it**: Dependency injection, auto-generated.
- **retrofit & json_annotation**: Network calls with type-safe APIs.
Run `flutter pub get` post-clone. For DI setup:
```bash
flutter packages pub run build_runner build --delete-conflicting-outputs
```
This generates `injection.dart`. Initialize in `main.dart`:
```dart
import 'package:injectable/injectable.dart';
import 'injection.dart';
void main() {
configureDependencies(Environment.prod);
runApp(MyApp());
}
```
Pro tip: Use `Environment.dev` for mocking in tests.
## How to Add a New Feature?
Let's build a 'users' feature step-by-step:
1. **Create folder**: `lib/src/features/users/` with `data/`, `domain/`, `presentation/`.
2. **Domain layer**:
- `entities/user.dart`: Pure data class with freezed.
```dart
import 'package:freezed_annotation/freezed_annotation.dart';
@freezed
class User with _$User {
factory User({required String id, required String name}) = _User;
}
```
- `repositories/user_repository.dart`: Abstract interface.
```dart
abstract class UserRepository {
Future<Either<Failure, User>> getUser(String id);
}
```
- `usecases/get_user.dart`: Business logic.
```dart
class GetUser implements UseCase<User, Params> {
final UserRepository repository;
@override
Future<Either<Failure, User>> call(Params params) =>
repository.getUser(params.id);
}
```
3. **Data layer**:
- `datasources/user_remote_datasource.dart`: Retrofit API.
- `repositories/user_repository_impl.dart`: Concrete impl wiring datasources.
4. **Presentation**:
- `bloc/user_bloc.dart`: Handles events, yields states.
```dart
class UserBloc extends Bloc<UserEvent, UserState> {
final GetUser getUser;
UserBloc(this.getUser) : super(UserInitial()) {
on<GetUserEvent>(
(event, emit) async {
final result = await getUser(Params(event.id));
result.fold(
(l) => emit(UserError(l.message)),
(r) => emit(UserLoaded(r)),
);
},
);
}
}
```
- `pages/user_page.dart`: UI consuming BLoC.
Register in DI with `@injectable`, rebuild runner. Add route in `app_router.dart`.
## Running and Testing the Template
Clone from [GitHub](https://github.com/FilledStacks/flutter-clean-architecture-feature-first-bloc):
```bash
git clone https://github.com/FilledStacks/flutter-clean-architecture-feature-first-bloc.git
cd flutter-clean-architecture-feature-first-bloc
flutter pub get
dart run build_runner build
flutter run
```
Example: 'posts' feature demos fetching, displaying posts with loading/error states. Tests? Each layer unit-tested:
- Domain: Mock repo, verify usecase.
- Data: Mock datasource.
- BLoC: bloc_test package.
## Benefits in Real-World Scenarios
- **Scalability**: Team of 10? Features assigned per dev, no merge hell.
- **Testability**: 90%+ coverage easy, pure functions everywhere.
- **Onboarding**: New hire? Dive into one feature.
- **Migration**: Swap HTTP for GraphQL? Only data layer changes.
E.g., fintech app: 'payments' feature isolated, compliant logic in domain.
## Common Pitfalls and Tips
- **Over-abstraction**: Start simple; add layers as complexity grows.
- **DI Hell**: Injectable shines—lazy/singleton scopes.
- **State Overkill**: Use Cubit for simple, BLoC for events.
- **Freezed Pitfalls**: Annotate properly, run builder.
Extend with Riverpod if needed, but BLoC's battle-tested.
## Scaling to Production
- Env configs: `.env` files.
- Analytics: Integrate Firebase in core.
- CI/CD: GitHub Actions for build_runner.
This template isn't rigid—adapt! Fork the [repo](https://github.com/FilledStacks/flutter-clean-architecture-feature-first-bloc) and iterate.
Word count: ~1050. Ready to build production-grade Flutter apps?
<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>