Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

flutter-base — Clean Architecture + BLoC Base

A production-ready Flutter starter with Clean Architecture, BLoC, and functional error handling. Small enough to read in one sitting, structured enough to scale to a 100-screen app.

Distilled from patterns I've used on large production Flutter apps (movie booking, global eSIM, e-commerce) — rewritten from scratch as a clean, reusable template.

Architecture at a glance

        presentation            domain              data
  ┌─────────────────────┐ ┌───────────────┐ ┌──────────────────────┐
  │  Page → Bloc        │→│ UseCase       │→│ Repository impl      │
  │  (states, events)   │ │ Repository    │ │  ├─ RemoteSource(Dio)│
  │                     │ │  (interface)  │ │  └─ Model(fromJson)  │
  └─────────────────────┘ └───────────────┘ └──────────────────────┘
          Flutter            pure Dart            packages/IO

The dependency rule: arrows point inward. domain imports nothing from Flutter, Dio, or JSON — which is why every layer tests in isolation.

Key decisions

Concern Choice Why
State management flutter_bloc (sealed events/states) Exhaustive switch in UI — a new state won't compile until handled
Errors dartz Either<Failure, T> Failures are values; exceptions die at the repository boundary
DI get_it composition root One file (core/di/injector.dart) wires interfaces to impls
Networking Dio with env-driven base URL --dart-define=API_BASE_URL=..., no hardcoded URLs
Theming Material 3 ColorScheme.fromSeed One seed color; light & dark stay in sync
Testing bloc_test + mocktail Bloc tests run in milliseconds with a mocked use case

Folder structure

lib/
├── main.dart                     # tiny: init DI, runApp
├── core/
│   ├── di/injector.dart          # composition root (get_it)
│   ├── error/failures.dart       # sealed Failure values
│   ├── network/dio_client.dart   # Dio factory + interceptor hook point
│   └── theme/app_theme.dart      # M3 light/dark from one seed
└── features/users/               # copy this folder to add a feature
    ├── domain/    entities · repository interface · usecases
    ├── data/      models(fromJson) · remote source · repository impl
    └── presentation/  bloc · pages · widgets

The flow of one request

  1. Page dispatches UsersRequested
  2. Bloc calls GetUsers() use case
  3. Use case calls UserRepository (interface)
  4. UserRepositoryImpl calls Dio source, catches exceptions → Either
  5. Bloc folds Either into UsersLoaded / UsersError
  6. UI switches over sealed states — no if-soup, no missed cases

Getting started

git clone https://github.com/rai-ms/flutter-base my_app && cd my_app
flutter pub get
flutter run --dart-define=API_BASE_URL=https://your-api.com
flutter test          # bloc tests, milliseconds

Adding a feature

Copy lib/features/users → rename → register its pieces in core/di/injector.dart. Each feature is self-contained; deleting its folder removes it completely.

License

MIT — use it for anything.

About

Production-ready Flutter base — Clean Architecture, BLoC (sealed states), Either-based error handling, get_it DI, bloc_test suite

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages