What is the Repository pattern?
Short Answer
The Repository pattern puts a single abstraction in front of however data is actually fetched or stored — network, local cache, database — so the rest of the app talks to one consistent interface and doesn't care where the data comes from.
Instead of widgets or use cases calling an HTTP client directly, they depend on a repository interface like `abstract class UserRepository { Future<User> getUser(String id); }`. The concrete implementation decides internally whether to hit the network, read a local cache, or merge both (e.g. return cached data immediately, then refresh from network) — that decision is invisible to everything calling the repository.
This gives you two big wins: you can swap the underlying data source (REST today, GraphQL tomorrow, or add an offline cache) without touching any calling code, and you can substitute a fake/mock repository in tests so business logic tests never make real network calls.
Code Example
abstract class UserRepository {
Future<User> getUser(String id);
}
class UserRepositoryImpl implements UserRepository {
UserRepositoryImpl(this._api, this._cache);
final ApiClient _api;
final LocalCache _cache;
@override
Future<User> getUser(String id) async {
final cached = _cache.getUser(id);
if (cached != null) return cached;
final user = await _api.fetchUser(id);
_cache.saveUser(user);
return user;
}
}Common Mistakes
- ×Creating a repository that just forwards every call to one data source with no added logic — at that point it's not adding value over calling the data source directly.
- ×Leaking data-source-specific types (like an HTTP response model) out through the repository interface instead of returning domain entities.
Related Questions
What is Clean Architecture and how does it apply to Flutter?
Clean Architecture separates an app into independent layers — typically presentation, domain, and data — where inner layers (business logic) never depend on outer layers (UI, frameworks, databases), so business rules can be tested and reused independently of Flutter itself.
What is Dependency Injection and how is it used in Flutter?
Dependency Injection means a class receives its dependencies from the outside (via constructor or a container) instead of creating them itself, which decouples classes from concrete implementations and makes them easy to test with substitutes.