๐Ÿ”— lynk_io

A scalable and reusable API communication layer built with Dio for Flutter apps.
Designed to simplify API calls, error handling, and dependency setup โ€” following Clean Architecture, modular design, and SOLID principles.


๐Ÿš€ Features

  • Generic GET, POST, PUT, PATCH, DELETE support
  • Clean separation of concerns using SOLID principles
  • Global error handling with structured exceptions
  • Built-in pluggable logger
  • Plug-and-play dependency injection via get_it
  • .env based API URL configuration (cross-platform)
  • Optional auth interceptor via getToken() callback
  • Multipart file upload / download utilities
  • Easily extensible and testable structure

๐Ÿ“ฆ Installation

Add this to your pubspec.yaml:

dependencies:
  lynk_io: ^0.0.5 # latest
  flutter_dotenv: ^5.1.0

๐Ÿงฑ Environment Setup

To dynamically manage environments like dev, staging, and production:

  1. Create a .env file at the root of your Flutter app:
API_BASE_URL=<your-server-url>
  1. Register the .env file in your pubspec.yaml:
flutter:
  assets:
    - .env
  1. Load the environment and set up dependencies before runApp():
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:lynk_io/lynk_io.dart';

void main() async {
  await dotenv.load();
  final baseUrl = dotenv.env['API_BASE_URL'] ?? '';

  setupDependencies(
    baseUrl: baseUrl,
    getToken: () async {
      // Replace with your token logic
      return 'your-auth-token';
    },
  );

  runApp(MyApp());
}

๐Ÿ›ก๏ธ Token-based Auth Interceptor

The package supports authenticated requests via an optional getToken() function:

setupDependencies(
  baseUrl: baseUrl,
  getToken: () async {
    // fetch your token securely
    return await storage.read(key: 'accessToken');
  },
);

All requests will automatically include:

Authorization: Bearer <your-token>

๐Ÿ“ฆ Example API Usage

Define a model class:

class User {
  final int id;
  final String name;

  User({required this.id, required this.name});

  factory User.fromJson(Map<String, dynamic> json) =>
      User(id: json['id'], name: json['name']);
}

Fetch user data:

final api = locator<ApiClient>();

final result = await api.get<User>(
  endpoint: '/user/<user-id>',
  fromJson: User.fromJson,
);

if (result.isSuccess) {
  AppLogger.log("User: ${result.data!.name}");
} else {
  AppLogger.error("Error: ${result.error!.message}");
}

๐Ÿ“ค POST Request Example

final result = await api.post<User>(
  endpoint: '/users',
  body: {
    'name': 'Siva G',
    'email': 'siva@example.com',
  },
  fromJson: User.fromJson,
);

๐Ÿ”„ Other Supported Methods

โœ… GET<T>()

api.get<T>(
  endpoint: '/items',
  fromJson: T.fromJson,
);

โœ… POST<T>()

api.post<T>(
  endpoint: '/items',
  body: {...},
  fromJson: T.fromJson,
);

โœ… PUT<T>()

api.put<T>(
  endpoint: '/items/1',
  body: {...},
  fromJson: T.fromJson,
);

โœ… PATCH<T>()

api.patch<T>(
  endpoint: '/items/1',
  body: {...},
  fromJson: T.fromJson,
);

โœ… DELETE<T>()

api.delete<T>(
  endpoint: '/items/1',
  fromJson: T.fromJson,
);

All methods return a unified ApiResponse<T> that encapsulates success and error data.


๐Ÿ“ File Upload & Download

Upload a file:

final api = locator<ApiClient>();
await api.files.uploadFile(
  'path/to/file.jpg',
  '/upload-endpoint',
);

Download a file:

await api.files.downloadFile(
  'https://server.com/file.pdf',
  '/local/save/path/file.pdf',
);

๐Ÿงช Testing & Extensibility

  • Easily mock the ApiClient for tests
  • Pass custom interceptors via setupDependencies(...)
  • Add more interceptors like caching, logging, etc.

๐Ÿ‘จโ€๐Ÿ’ป Author

Built and maintained with โค๏ธ by Siva G
Feel free to contribute, fork, or drop feedback!


Libraries

lynk_io