← Flutter Reference

What is the Repository Pattern in Flutter?

Architecture · Intermediate

By Abdelrahman Saed — Senior Mobile Engineer. Last updated: 2026-08-10.

The repository pattern is a structural design pattern that abstracts data access behind a uniform interface. Instead of the UI or business logic calling APIs, databases, or caches directly, they call a repository — which decides where data comes from, handles caching, and manages offline behaviour.

How it works

A repository defines a contract (abstract class) in the domain layer and provides concrete implementations in the data layer:

// The contract — what the app needs
abstract class StoryRepository {
  Future<Either<Failure, List<Story>>> fetchStories();
  Stream<List<Story>> watchStories();
}

// The implementation — how the app gets it
class StoryRepositoryImpl implements StoryRepository {
  StoryRepositoryImpl(this._remote, this._local);
  final StoryRemoteDataSource _remote;
  final StoryLocalDataSource _local;

  @override
  Future<Either<Failure, List<Story>>> fetchStories() async {
    try {
      final stories = await _local.cachedStories();    // offline-first read
      unawaited(_remote.refreshInBackground());         // sync, never blocks UI
      return Right(stories);
    } on CacheException catch (e) {
      return Left(CacheFailure(e.message));
    }
  }
}

The BLoC or Cubit never sees StoryRemoteDataSource or StoryLocalDataSource. It calls repository.fetchStories() and gets data. The repository is where offline-first logic, caching strategy, and error mapping live.

At iStoria, every feature has at least one repository. The repository is the only thing the presentation layer knows about when it needs data. Whether that data comes from the API, the local Drift database, or PowerSync's sync layer is the repository's business — nobody else's.

When to use it

Use the repository pattern when:

  • You have more than one data source (API + cache, or local DB + remote).
  • You want to swap implementations (mock for testing, different backend).
  • You're building an offline-first app — the repository is where the offline logic lives.

Skip it when:

  • The app has a single data source and no caching needs — a direct API call is simpler.
  • You're prototyping and the data layer is a single function.

Strengths

  • Abstracts data sources — the UI doesn't know or care where data comes from
  • Centralizes caching and offline logic — one place to manage, not per-screen
  • Swappable implementations — mock the repository in tests without mocking the database
  • Pairs naturally with Either<Failure, T> — errors are values, not exceptions
  • Scales to 50+ modules — every feature has the same data-access shape

Tradeoffs

  • Adds a layer of indirection — one more interface and implementation per feature
  • Can feel like ceremony for trivial data access (one API call, no caching)
  • Requires discipline to keep the contract lean — dumping every API call on one repository creates a god object

Comparisons

Related articles

Related case studies

FAQ

What is the difference between a repository and a data source?

A data source is one source of data (an API client, a local database, a cache). A repository orchestrates multiple data sources behind one interface. The repository decides: read from cache first, fall back to API, update cache. Data sources don't know about each other; the repository does.


Available for hire. Abdelrahman Saed is a Senior Mobile Engineer (Flutter) — open to full-time, fractional, contract, or advisory work. Hire me →

Book a 20-minute call · Download the CV (PDF) · See how I work