A full-featured Pokémon encyclopaedia built with Flutter, consuming the PokéAPI. Designed to demonstrate production-quality architecture, comprehensive testing, and polished UX patterns.
🔗 Also check out MovieDB-Android — a similar project modularised by feature instead of by layer.
| Concern | Library |
|---|---|
| State management | flutter_bloc |
| Networking | dio |
| Local cache | sqflite |
| Image caching | cached_network_image |
| DI | get_it |
| Functional types | dartz |
| Animations | Flutter built-ins |
| Localisation | flutter_localizations / intl |
| Testing | mockito, bloc_test |
The project is modularised by layer following Clean Architecture principles, with each layer compiled as its own Dart package to enforce strict dependency boundaries.
┌─────────────────────────────────────┐
│ Presentation │ Widgets, Pages, BLoC
├─────────────────────────────────────┤
│ Domain │ Use Cases, Entity Models
├─────────────────────────────────────┤
│ Repository │ Coordinates Data & Local sources
├──────────────────┬──────────────────┤
│ Data │ Local │ Dio (REST API) │ SQLite (cache)
└──────────────────┴──────────────────┘
- Presentation — Flutter widgets, pages, and BLoC cubits. No business logic.
- Domain — Pure Dart. Contains use cases and entity models. Zero Flutter dependencies.
- Repository — Orchestrates between the remote and local sources, handles mapping and error handling.
- Data — Fetches data from PokéAPI using Dio and maps responses to domain models.
- Local — Reads/writes to an SQLite database via
sqflitefor offline caching.
Comprehensive unit tests across every module, with integration tests that validate the full data flow end-to-end.
Integration tests validating interaction between all components:
Full support for system light and dark themes.
Skeleton shimmer placeholders keep the UI responsive while data is loading.
CachedNetworkImage provides smooth, flicker-free image loading. The list uses a custom scroll controller to hide the app bar on scroll down and reveal it again on a slight scroll up.
Data is exposed as a Stream so the UI can render cached results instantly while a live API fetch runs in parallel. A loading indicator communicates the in-progress refresh.
Built with flutter_localizations and AppL10n for straightforward internationalisation.
Semantics widgets and Tooltips are used throughout to support screen readers and assistive technologies.





