rt_api 1.0.5 copy "rt_api: ^1.0.5" to clipboard
rt_api: ^1.0.5 copied to clipboard

A RulTech API project.

RT API #

A standardized API service layer for Flutter applications built on GetX's GetConnect, following RulTech's architecture pattern.

Features #

  • Callback-Based Architecture: Two initialization patterns (function-based and mixin-based) for flexible API lifecycle handling
  • Automatic Error Handling: Built-in handling for network errors, timeouts, and HTTP exceptions
  • Connectivity Detection: Automatic internet connectivity checks before API calls
  • Bearer Token Management: Automatic token handling with GetStorage integration
  • Configurable Status Codes: Support for custom allowed status codes
  • Debug Logging: Comprehensive debug logging that only activates in debug mode
  • Fluent API: Chainable configuration methods for clean, readable code
  • Snackbar Support: Optional automatic error snackbar display

Getting Started #

Prerequisites #

Add the following dependencies to your pubspec.yaml:

dependencies:
  get: ^4.6.6
  get_storage: ^2.1.1
  connectivity_plus: ^6.0.5

Installation #

Add rt_api to your pubspec.yaml:

dependencies:
  rt_api:
    path: ../rt_api  # or your package path

Base URL Configuration #

Configure the base URL in one of two ways:

await GetStorage.init();
GetStorage().write("baseUrl", "https://api.example.com/");

Option 2: Override in AppString mixin

// In lib/rt_api/api_strings.dart
mixin AppString {
  String get baseUrl => "https://api.example.com/";
}

Usage #

Basic Setup #

Function-Based Approach

final api = ApiService.init(
  ApiHandler(
    apiStartCallback: () => print('API Started'),
    apiSuccessfulCallback: (ApiSuccess data) {
      print('Success: ${data.data}');
    },
    apiFailedWithMsg: (ApiError error) {
      print('Error: ${error.message}');
    },
    apiFailedCheckYourInternet: (ApiError error) {
      print('No Internet: ${error.message}');
    },
  ),
  showSnackBar: true,
  baseUrl: "https://api.example.com/", // Optional
);

Mixin-Based Approach (Preferred for Controllers)

class MyController extends GetxController with ClassApiHandler {
  late final ApiService api;

  @override
  void onInit() {
    super.onInit();
    api = ApiService.initWithThis(this);
  }

  @override
  void apiSuccessfulCallback(ApiSuccess apiData) {
    // Handle success
    print('Data: ${apiData.data}');
  }

  @override
  void apiFailedWithMsg(ApiError error) {
    // Handle error
    print('Error: ${error.message}');
  }

  @override
  void apiFailedCheckYourInternet(ApiError error) {
    // Handle no internet
    print('Check your connection');
  }

  @override
  void apiStartCallback() {
    // API call started
    print('Loading...');
  }
}

Making API Calls #

GET Request

// Simple GET
await api.getRequest('/users');

// GET with query parameters
await api.getRequest(
  '/users',
  query: {'page': '1', 'limit': '10'},
);

// GET with custom headers
await api
  .setHeaders({'Custom-Header': 'value'})
  .getRequest('/users');

POST Request

// POST with body
await api.postRequest(
  '/login',
  {
    'email': 'user@example.com',
    'password': 'password123',
  },
);

// POST with bearer token
await api
  .setBearerToken()
  .postRequest('/create-post', postData);

PUT Request

await api
  .setBearerToken()
  .putRequest('/users/123', {
    'name': 'John Doe',
    'email': 'john@example.com',
  });

PATCH Request

await api
  .setBearerToken()
  .patchRequest('/users/123', {
    'name': 'Jane Doe',
  });

DELETE Request

await api
  .setBearerToken()
  .deleteRequest('/users/123');

Fluent Configuration #

Chain configuration methods for clean setup:

await api
  .setHeaders({'Accept': 'application/json'})
  .setBearerToken()
  .setTimeOut(30)
  .setMaxAuthRetries(2)
  .setShowSnackBar(false)
  .postRequest('/endpoint', body);

Debug Logging #

The package includes comprehensive debug logging that only activates in debug mode (when kDebugMode is true). By default, debug logging is enabled.

Control Debug Logging

// Debug logging is ON by default
final api = ApiService.init(handler);

// To disable debug logging
api.showDebugLog(false);

// To enable debug logging
api.showDebugLog(true);

Debug Log Output

When enabled in debug mode, you'll see detailed logs:

✅ RT-API-Call: >> Initializing ApiService
✅ RT-API-Call: >> Base URL: https://api.example.com/
✅ RT-API-Call: >> POST Request - URL: /api/login
✅ RT-API-Call: >> POST Body: {email: user@example.com, password: ***}
✅ RT-API-Call: >> Sending POST request to: https://api.example.com/api/login
✅ RT-API-Call: >> POST Response - Status: 200
✅ RT-API-Call: >> Success Response - Data Type: _Map<String, dynamic>
✅ RT-API-Call: >> API Success - Status: 200, Message: Successful

Error logs are prefixed with ❌:

❌ RT-API-Call: ERROR >> No internet connection detected
❌ RT-API-Call: ERROR >> POST Exception: Connection timeout

Important: Debug logs are automatically disabled in release builds for optimal performance.

Custom Allowed Status Codes #

By default, only status code 200 is considered successful. You can customize this:

final api = ApiService.init(
  handler,
  allowedStatusCodes: [200, 201, 204], // Custom success codes
);

Bearer Token Authentication #

// Store token in GetStorage
GetStorage().write('apiToken', 'your-auth-token');

// Use bearer token in requests
await api
  .setBearerToken()
  .getRequest('/protected-endpoint');

Response Handling #

ApiSuccess Structure

class ApiSuccess<T> {
  int statusCode = 200;
  String message = "Successful";
  bool result = false;
  T? data;  // Generic typed response data
}

ApiError Structure

class ApiError {
  int statusCode = codeResponseNull;
  String message = "Error: ";
  dynamic errorObject;  // Raw error response for custom parsing
}

Status Codes #

const codeNoInternet = 100;           // No internet connection
const codeResponseNull = 103;         // Null response body
const codeTimeOut = 104;              // Request timeout
const codeUnknown = 105;              // Unknown error
const codePerformLogoutOnTokenExpires = 412;  // Token expired
const codePerformValidStatusCode = 400;       // Validation error

Complete Example #

import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:rt_api/rt_api.dart';

class UserController extends GetxController with ClassApiHandler {
  late final ApiService api;
  final users = <User>[].obs;
  final isLoading = false.obs;

  @override
  void onInit() {
    super.onInit();
    api = ApiService.initWithThis(this);
    fetchUsers();
  }

  Future<void> fetchUsers() async {
    await api
      .setBearerToken()
      .setTimeOut(30)
      .getRequest('/users');
  }

  Future<void> createUser(Map<String, dynamic> userData) async {
    await api
      .setBearerToken()
      .postRequest('/users', userData);
  }

  @override
  void apiStartCallback() {
    isLoading.value = true;
  }

  @override
  void apiSuccessfulCallback(ApiSuccess apiData) {
    isLoading.value = false;
    if (apiData.data is List) {
      users.value = (apiData.data as List)
          .map((json) => User.fromJson(json))
          .toList();
    }
  }

  @override
  void apiFailedWithMsg(ApiError error) {
    isLoading.value = false;
    Get.snackbar('Error', error.message);
  }

  @override
  void apiFailedCheckYourInternet(ApiError error) {
    isLoading.value = false;
    Get.snackbar('Connection Error', 'Please check your internet');
  }
}

Technical Information #

Environment Requirements #

  • Flutter: 3.32.0 (minimum 3.2.3 recommended)
  • Dart SDK: 3.8.0 (constraint: >=3.2.3 <=4.0.0)
  • Supported Platforms: Android, iOS, macOS, Web
  • Development Environment:
    • macOS 26.0.1 (darwin-arm64)
    • Xcode 26.1 (Build 17B55)
    • Android SDK 36.1.0
    • Java: OpenJDK 21.0.8
    • VS Code 1.105.1
    • Android Studio 2025.1

Dependencies #

Core Dependencies

  • get (^4.6.6): State management and HTTP client (GetConnect)
  • get_storage (^2.1.1): Local storage for base URL and auth token
  • connectivity_plus (^6.0.5): Network connectivity detection

Development Dependencies

  • flutter_lints (^4.0.0): Enforces Flutter best practices and code quality

All dependencies use caret (^) constraints for automatic minor/patch updates. Run flutter pub upgrade regularly to stay current.

Architecture Overview #

This package follows RulTech's architecture pattern: Architecture Snippet

Core Design Pattern: Callback-Based API Handler

The package uses two initialization patterns for handling API lifecycle callbacks:

  1. ApiHandler (Function-based): Pass callback functions to handle API lifecycle
  2. ClassApiHandler (Mixin-based): Implement mixin methods in your class for tighter integration

Response Flow Architecture

All API methods follow this pattern:

  1. Connectivity check → Returns ApiError with codeNoInternet (100) if offline
  2. HTTP request → Uses GetX's GetConnect methods
  3. Response processingprocessAndHandleResponse() determines success/error
  4. Callback invocation → Calls appropriate handler method (success or error)
  5. Return value → Returns ApiSuccess or ApiError for direct handling

Key Components #

Base URL Configuration Priority

  1. Constructor parameter baseUrl
  2. GetStorage with key "baseUrl"
  3. AppString mixin override

If none are set, throws UnimplementedError.

Status Code Handling

Custom status codes can be configured via allowedStatusCodes parameter:

ApiService(allowedStatusCodes: [200, 201, 204])

Default: [200]

Debug Logging Configuration

  • Production Safe: Uses kDebugMode constant (Flutter foundation)
  • Default State: Enabled (_showDebugLog = true)
  • Control Method: showDebugLog(bool)
  • Output Format: Prefixed with tag "RT-API-Call:", wrapped at 1024 characters
  • Performance: Zero overhead in release builds

Error Handling Strategy

All HTTP methods catch 5 exception types:

  • GetHttpException - GetConnect HTTP errors
  • UnauthorizedException - 401 responses
  • UnexpectedFormat - JSON parsing failures
  • Exception - Generic Dart exceptions
  • FlutterErrorDetails - Flutter framework errors

All converge to _handleError() which creates ApiError and invokes callbacks.

Package Structure #

rt_api/
├── lib/
│   ├── rt_api.dart              # Public API exports
│   └── rt_api/
│       ├── api_service.dart     # Main service class with HTTP methods
│       ├── api_strings.dart     # Base URL configuration mixin
│       ├── Handler/
│       │   ├── api_handler.dart       # Function-based callback handler
│       │   └── class_api_handler.dart # Mixin-based callback handler
│       └── helper/
│           ├── api_error.dart         # Error response model
│           ├── api_success.dart       # Success response model
│           └── get_connect_helper.dart # Abstract GetConnect wrapper
├── test/
│   └── rt_api_test.dart         # Unit tests
├── analysis_options.yaml        # Linter configuration
├── CHANGELOG.md                 # Version history
└── pubspec.yaml                 # Package dependencies

Design Principles #

1. Modular Approach

  • Single Responsibility: Each class/file handles one concern
    • ApiService → HTTP operations
    • ApiHandler/ClassApiHandler → Callback management
    • ApiError/ApiSuccess → Response models
  • Separation of Concerns: Business logic separated from API layer
  • Handler Pattern: Two initialization patterns for flexibility

2. Common Constants

  • Centralized Status Codes: All defined at top of api_service.dart
  • String Constants: Base URL in api_strings.dart mixin
  • No Magic Numbers: All values are named constants

3. No Deprecated Code

  • Current Dependencies: All packages use latest stable versions
  • Version Strategy: Caret (^) for automatic minor/patch updates
  • Migration Ready: When Flutter/Dart updates deprecate APIs, package updates immediately
  • Lint Enforcement: flutter_lints: ^4.0.0 enforces latest practices

4. Encapsulation

  • Private Members: Prefixed with underscore for internal use
  • Controlled Access: Public getters only when needed
  • Protected Methods: Internal methods are private
  • Clean Public API: Only necessary methods exposed via lib/rt_api.dart

5. Version Management

  • Latest Versions: Always use latest or any for library versions
  • Flexible Constraints: Caret (^) allows updates within major version
  • Regular Updates: Run flutter pub upgrade regularly
  • Breaking Change Policy: Only lock versions if breaking changes exist

Performance Considerations #

  • Zero Overhead in Production: Debug logging completely disabled in release builds
  • Efficient Connectivity Checks: Checks occur before expensive HTTP operations
  • Lazy Initialization: Resources initialized only when needed
  • Memory Efficient: Callbacks are optional, no forced object retention

Security Features #

  • Token Management: Secure token storage via GetStorage
  • Automatic Token Injection: Bearer token added to headers automatically
  • Token Expiration Handling: Status code 412 signals token expiration
  • No Token Logging: Sensitive data not included in debug logs

Testing #

Unit tests can be added to test/rt_api_test.dart. Recommended test patterns:

  • Mock GetStorage for offline/online scenarios
  • Test both ApiHandler and ClassApiHandler initialization
  • Verify callback invocation for all response types
  • Test connectivity detection

Contributing #

For issues and feature requests, please contact RulTech development team.

License #

See LICENSE file for details.

0
likes
115
points
96
downloads

Documentation

API reference

Publisher

verified publisherrultech.com

Weekly Downloads

A RulTech API project.

Homepage

License

MIT (license)

Dependencies

connectivity_plus, flutter, get, get_storage

More

Packages that depend on rt_api