Menu

Data Layer

Relevant source files

The Data Layer in this project template provides centralized access to external data sources through standardized interfaces. This layer is responsible for network communications, local storage, and error handling, serving as a bridge between raw data sources and the rest of the application.

For information about UI components that consume this data, see UI Components.

Overview

The Data Layer is part of the core packages (packages/cores/data) that provide foundational functionality to the entire application. It abstracts away the implementation details of data access, allowing the rest of the application to interact with data sources through a consistent API.

Sources: packages/cores/data/lib/src/network/provider/dio.dart packages/cores/data/lib/shared_preferences.dart

Network Communication

Dio HTTP Client

The template provides a preconfigured Dio HTTP client for making network requests. The client is provided through a Riverpod provider, making it easily accessible throughout the application.

Sources: packages/cores/data/lib/src/network/provider/dio.dart9-26

The Dio instance is configured with:

  • Base URL for GitHub API
  • Authorization headers when a token is available
  • Connection and receive timeouts
  • Logging interceptor for debugging

Safe Request Handling

The Data Layer extends Dio with a safeRequest method that wraps network requests with standardized error handling. This extension converts Dio-specific exceptions into application-specific exceptions.

Sources: packages/cores/data/lib/src/network/provider/dio.dart28-67

Usage example for the safe request method:

Future<void> fetchUser() async {
  state = const AsyncValue.loading();
  try {
    final result = await dio.safeRequest<User>(
      request: () => dio.get('/users/1'),
    );
    state = AsyncValue.data(data);
  } on AppException catch (e, stackTrace) {
    ref.read(appExceptionNotifierProvider.notifier).notify(e);
    state = AsyncValue.error(e, stackTrace);
  }
}

This approach provides several benefits:

  • Consistent error handling across the application
  • Conversion of third-party exceptions to application-specific exceptions
  • Clean API for consumers

API Authentication

The template includes a provider for GitHub access tokens, demonstrating how to handle API authentication.

Sources: packages/cores/data/lib/src/network/provider/github_access_token.dart

The token is retrieved from environment variables and provided to the Dio client through dependency injection with Riverpod.

Local Storage

SharedPreferences

The Data Layer provides access to device local storage through the sharedPreferencesProvider. This is a kept-alive (singleton) provider that gives access to the SharedPreferences instance.

Sources: packages/cores/data/lib/src/shared_preferences/provider/shared_preferences.dart

Note that in the template, the actual implementation for sharedPreferencesProvider throws an UnimplementedError. This is because the SharedPreferences instance needs to be initialized during app startup and then overridden in the provider. This initialization typically happens in the AppInitializer class.

Integration with Other Layers

The Data Layer interacts with several other components in the application architecture:

Layer/ComponentInteraction
Core LayerProvides exception types (NetworkException, AppException)
Model LayerDefines data entities that the Data Layer works with
Feature PackagesConsume the Data Layer through repositories
App InitializationInitializes and configures providers like SharedPreferences

Best Practices

When working with the Data Layer in this template:

  1. Create repositories for specific domains that use the provided data sources
  2. Use the safeRequest extension for all network requests to ensure consistent error handling
  3. Separate network and local storage logic from business logic
  4. Use Riverpod providers to make data sources accessible throughout the application
  5. Handle token-based authentication through the provided pattern