CCCI Utility

This documentation provides an overview of the CCCI Utils, including custom widgets, utility classes, and network handling.

Table of Contents

Utility Classes

Utility Classes

BaseApi

A base class for making API calls using the Dio HTTP client.

  • The BaseApi class provides an abstraction for performing standard HTTP operations such as GET, POST, PUT, and DELETE. It simplifies the process of interacting with RESTful APIs by using the Dio library, and it handles common tasks such as building routes, sending requests, and error handling.

Usage

  • The class can be used to build specific API services by passing a route and a DioClient instance. Each method supports optional parameters to customize requests, such as query parameters or request paths.
  • Example of usage:
final userApi = BaseApi('/users', dioClient);
final user = await userApi.getById('123');

Key Concepts

  • Dio: A powerful HTTP client for Dart, which allows easy HTTP requests.
  • Error Handling: Errors from the API requests are logged using AppLogger and rethrown for further handling in the app layer.
  • Customization: Each method allows customization with optional query parameters or paths, making it versatile for various API requests.

Parameters:

  • String route: The base route (or endpoint) for the API service. This is used as the starting point for all HTTP requests.
  • DioClient dioClient: An instance of the Dio HTTP client used to perform network requests.

Constructor:

  • BaseApi(this.route, this.dioClient):
    • Accepts a route and a dioClient for making requests to the API.

Methods:

  • getById: Sends a GET request to fetch a resource by its unique ID.
  • get: Sends a GET request to fetch resources, optionally with query parameters or a specific path.
  • post: Sends a POST request to create a new resource with a payload.
  • put: Sends a PUT request to update an existing resource by its unique ID.
  • delete: Sends a DELETE request to remove a resource by its unique ID.

Example of Extending the Base Class:

class UserApi extends BaseApi {
  UserApi(DioClient dioClient) : super('/users', dioClient);

  Future fetchUsers() => get();
}

In this example, the UserApi class extends BaseApi and uses the get method to fetch users from the /users endpoint.

Base Repository

A generic repository class for handling data retrieval and management from APIs.

The **BaseRepository** class provides an abstraction layer for interacting with APIs, allowing for consistent and reusable CRUD operations on data entities. It is designed to be used with model classes of type T, and it relies on a BaseApi service for making HTTP requests.

This class offers methods to retrieve a list of entities or a single entity by ID, using the provided BaseApi for the underlying HTTP operations. It also handles error logging via AppLogger.

Usage

  • The class is abstract and should be extended by specific repositories for individual data entities. Each repository should define the fromJson method, which is responsible for converting JSON responses into model objects.

Example of usage:

class UserRepository extends BaseRepository<User> {
  UserRepository(BaseApi api) : super(api, (json) => User.fromJson(json));
}

Key Concepts

  • BaseApi: The service used for making HTTP requests. This class uses BaseApi for all API interactions, such as GET, POST, PUT, and DELETE requests.
  • Model Conversion: The fromJson function is required to convert the JSON data returned by the API into the model objects of type T.
  • Error Handling: Errors are caught and logged using AppLogger, with the original error rethrown for higher-level handling.

Parameters:

  • BaseApi api: The API service used to perform HTTP requests.
  • T Function(Map<String, dynamic>) fromJson: A function that converts a JSON map into an instance of the model class T.

Constructor:

  • BaseRepository(this.api, this.fromJson):
    • Accepts an api instance and a fromJson function to initialize the repository.

Methods:

  • get: Retrieves a list of entities from the API using optional query parameters.
  • getById: Retrieves a single entity by its unique identifier, with optional query parameters.

Example of Extending the Base Class:

class ProductRepository extends BaseRepository<Product> {
  ProductRepository(BaseApi api) : super(api, (json) => Product.fromJson(json));

  Future<List<Product>> fetchProducts({Map<String, dynamic>? filters}) {
    return get(queryParameters: filters);
  }

  Future<Product> fetchProductById(String id) {
    return getById(id);
  }
}

In this example, the ProductRepository extends BaseRepository to handle product-related API calls, such as fetching a list of products or a single product by ID.

Base Service Provider

An abstract base class for creating service providers using Riverpod.

This class provides the foundation for service providers by accepting a ProviderRef in the constructor. It allows access to the provider's context and resources, and can be extended to create various service providers.

The ProviderRef is stored in a private field ref, allowing derived classes to access it for various purposes such as managing state, listening to other providers, or performing dependency injection.

Usage

To create a service provider, extend this BaseServiceProvider class and implement the specific business logic in your custom provider class.

Example:

class AuthServiceProvider extends BaseServiceProvider {
  AuthServiceProvider(ProviderRef ref) : super(ref);

  void signIn(String email, String password) {
    // Logic to handle user sign-in using ref to interact with providers.
  }
}

This allows you to centralize logic related to specific functionalities, such as authentication, networking, or data storage, and share this functionality across your application using Riverpod.

Key Concepts

  • ProviderRef: This is a handle to interact with other providers or manage state within Riverpod. The ProviderRef object is essential for accessing other providers and interacting with the global provider system.

  • Extendability: The BaseServiceProvider can be extended to create specialized service providers. Each derived class will inherit access to the ref field, allowing for dependency injection or interactions with other providers.

  • Riverpod: This class is part of a project that uses Riverpod for state management, which offers a declarative way to manage dependencies and app state in Flutter.

Parameters:

  • ProviderRef ref: The reference to the Riverpod provider that this service provider will use for its logic.

Properties:

  • ref: A private field of type ProviderRef which provides access to other providers within the Riverpod framework.

Constructor:

  • BaseServiceProvider(ProviderRef ref):
    • Accepts a ProviderRef as a parameter and assigns it to the private field ref for future use within the derived service provider.

Example of Extending the Base Class

class NetworkServiceProvider extends BaseServiceProvider {
  NetworkServiceProvider(ProviderRef ref) : super(ref);

  Future<void> fetchData() async {
    // Use ref to access other providers or state management.
    // Example: final apiService = ref.read(apiServiceProvider);
  }
}

In this example, the NetworkServiceProvider extends the BaseServiceProvider and uses the ref to interact with other providers like apiServiceProvider.

AppLogger

The AppLogger class is a utility class for logging messages in the application at different levels of severity. It provides static methods for logging debug, info, warning, error, and verbose messages. Additionally, it includes methods for logging API requests, responses, and errors.

Usage:

AppLogger.debug('Debug message');
AppLogger.error('Error message');
AppLogger.logApiRequest(url: 'https://api.example.com', method: 'GET');

RouteHandler

The RouteHandler class is a utility class for handling navigation operations within the application. It provides methods for pushing, popping, and replacing routes in the navigator stack. These methods facilitate seamless navigation between different screens and components within the application.

RouteHandler.push(context, HomePage());
RouteHandler.pop(context);

DioExceptions

The DioExceptions class is a custom exception class that handles errors from Dio requests. It provides custom error messages based on different types of Dio exceptions encountered, including connection timeouts, receive timeouts, bad responses, and unknown errors.

Usage:

try {
  // Dio request
} catch (e) {
  if (e is DioException) {
    final dioException = DioExceptions.fromDioError(e);
    print(dioException.message);
  }
}

DioClient

The DioClient class encapsulates functionality for making HTTP requests using the Dio package. It includes methods for making GET, POST, PUT, and DELETE requests with configurable options and interceptors. This class provides a convenient and efficient way to interact with APIs and handle network requests within the application.

Dart Dependencies

To use the DioClient, you need to have the following Dart packages in your pubspec.yaml:

dependencies:
    dio
    flutter_dotenv

Configuration Files

Make sure you have the necessary configuration files:

  1. . env

    Usage:
    BASE_URL=<your_base_url>
    
  2. config.json

    Usage:
    {
      "development": {
        "current": true,
        "baseUrl": "https://dev.example.com"
      },
      "production": {
        "current": false,
        "baseUrl": "https://api.example.com"
      }
    }