Back to Blog
Mobile Development

Flutter Clean Architecture: Feature-First with BLoC for Scalable, Maintainable Apps

Claude Directory November 30, 2025
5 views

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 with dartz Either<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), GetTrivia use case.
  • Data: NumberTriviaModel (fromJson), NumberTriviaRemoteDataSource, concrete NumberTriviaRepositoryImpl.
  • Presentation: NumberTriviaBloc with 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 in injection_container.dart.

Real-world tip: Use flutter pub deps to visualize your graph and avoid bloat.

Getting Started: Step-by-Step Setup

  1. Clone and Run: Grab the template: git clone https://github.com/SimformSolutionsPvtLtd/flutter_clean_architecture_feature_first.git

    flutter pub get
    flutter pub run build_runner build  # For code gen if using freezed
    flutter run
    
  2. 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() in main.dart.

  3. 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?

  1. Create features/weather/ with data/domain/presentation subfolders.
  2. Define WeatherEntity, GetWeather use case.
  3. Implement data sources (e.g., WeatherRemoteDataSource with http).
  4. Wire BLoC: Events, states, inject use case.
  5. Add screen in presentation/pages/ using BlocProvider.
  6. Register DI: Data source → Repo → Use case.
  7. 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 deferred imports.
  • 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>
GitHub Project

Comments

More Blog

View all
Claude for Developers

Building 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.

C
Claude Directory
2
Model Comparisons

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

C
Claude Directory
1
Enterprise

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

C
Claude Directory
1
Claude Code

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.

C
Claude Directory
1
Claude for Developers

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.

C
Claude Directory
1
Claude Best Practices

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.

C
Claude Directory
1