apix 1.4.0 copy "apix: ^1.4.0" to clipboard
apix: ^1.4.0 copied to clipboard

Production-ready Flutter/Dart API client with auth refresh queue, exponential retry, smart caching and error tracking (Sentry-ready). Powered by Dio.

example/example.dart

/// Apix Example
///
/// This example demonstrates the basic usage of the apix package,
/// including SecureTokenProvider for secure token storage.
library;

import 'package:apix/apix.dart';
import 'package:flutter/material.dart';

/// Simple example showing API client creation and usage.
void main() async {
  // ============================================================
  // SECURE TOKEN STORAGE
  // ============================================================
  // SecureTokenProvider uses flutter_secure_storage under the hood
  final tokenProvider = SecureTokenProvider();

  // Create an API client with authentication and retry
  final client = ApiClientFactory.create(
    baseUrl: 'https://api.example.com',
    connectTimeout: const Duration(seconds: 30),
    receiveTimeout: const Duration(seconds: 30),
    // Authentication configuration (v1.0.1+)
    authConfig: AuthConfig(
      tokenProvider: tokenProvider,
      // Simplified refresh flow (recommended)
      refreshEndpoint: '/auth/refresh',
      onTokenRefreshed: (response) async {
        final data = response.data as Map<String, dynamic>;
        await tokenProvider.saveTokens(
          data['access_token'] as String,
          data['refresh_token'] as String,
        );
      },
    ),
    // Retry configuration (v1.0.1+)
    retryConfig: const RetryConfig(
      maxAttempts: 3,
      retryStatusCodes: [500, 502, 503, 504],
    ),
    // Cache configuration (v1.0.1+)
    cacheConfig: CacheConfig(
      strategy: CacheStrategy.networkFirst,
      defaultTtl: const Duration(minutes: 5),
    ),
    // Logger configuration (v1.0.1+)
    loggerConfig: const LoggerConfig(
      level: LogLevel.info,
      redactedHeaders: ['Authorization'],
    ),
    // Error tracking configuration (v1.0.1+)
    errorTrackingConfig: ErrorTrackingConfig(
      onError: (Object e,
          {StackTrace? stackTrace,
          Map<String, dynamic>? extra,
          Map<String, String>? tags}) async {
        debugPrint('Error captured: $e');
      },
    ),
    // Metrics configuration (v1.0.1+)
    metricsConfig: MetricsConfig(
      onMetrics: (metrics) {
        debugPrint(
            'API: ${metrics.method} ${metrics.path} - ${metrics.durationMs}ms');
      },
    ),
  );

  // ============================================================
  // TYPED RESPONSE METHODS (3 levels)
  // ============================================================

  try {
    // --- Level 1: Standard (raw Response) ---
    final response = await client.get<Map<String, dynamic>>('/users/1');
    debugPrint('Raw: ${response.data}');

    // --- Level 2: Parse & Decode (formats response.data) ---
    // Decode: for JSON objects (tear-off friendly)
    final user = await client.getAndDecode('/users/1', User.fromJson);
    debugPrint('User: ${user.name}');

    // Parse: for any type (flexible)
    final count = await client.getAndParse(
      '/users/count',
      (data) => data as int,
    );
    debugPrint('Count: $count');

    // POST variants
    final created = await client.postAndDecode(
      '/users',
      {'name': 'John'},
      User.fromJson,
    );
    debugPrint('Created: ${created.name}');

    // --- Level 3: Data Methods (envelope unwrapping) ---
    // For APIs returning: { "data": { ... } }
    // Extracts response.data[dataKey] then formats

    // Single object from envelope
    final profile = await client.getAndDecodeData('/profile', User.fromJson);
    debugPrint('Profile: ${profile.name}');

    // Nullable: returns null if data key is null
    final maybe =
        await client.getAndDecodeDataOrNull('/profile', User.fromJson);
    debugPrint('Maybe: ${maybe?.name}');

    // List from envelope: { "data": [{ ... }, { ... }] }
    final users = await client.getListAndDecodeData('/users', User.fromJson);
    debugPrint('Users: ${users.length}');

    // List with fallback to empty
    final empty =
        await client.getListAndDecodeDataOrEmpty('/users', User.fromJson);
    debugPrint('Users or empty: ${empty.length}');

    // Parse variant for non-JSON: { "data": ["admin", "editor"] }
    final roles = await client.getListAndParseData(
      '/roles',
      (item) => item as String,
    );
    debugPrint('Roles: $roles');

    // POST Data variants
    final searched = await client.postListAndDecodeData(
      '/search',
      {'query': 'john'},
      User.fromJson,
    );
    debugPrint('Search results: ${searched.length}');
  } on HttpException catch (e) {
    debugPrint('HTTP Error: ${e.statusCode}');
  } on NetworkException catch (e) {
    debugPrint('Network Error: ${e.message}');
  }

  // Use Result type for functional error handling
  final result = await client.get<Map<String, dynamic>>('/users').getResult();
  result.when(
    success: (response) => debugPrint('Got ${response.data}'),
    failure: (error) => debugPrint('Error: ${error.message}'),
  );

  // ============================================================
  // TOKEN MANAGEMENT
  // ============================================================
  // After login, save tokens
  await tokenProvider.saveTokens('access_token_here', 'refresh_token_here');

  // Access underlying storage for other secrets
  await tokenProvider.storage.write('firebase_token', 'firebase_token_here');

  // On logout, clear tokens
  await tokenProvider.clearTokens();

  // Clean up
  client.close();
}

/// Example user model.
class User {
  final int id;
  final String name;
  final String email;

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

  factory User.fromJson(Map<String, dynamic> json) {
    return User(
      id: json['id'] as int,
      name: json['name'] as String,
      email: json['email'] as String,
    );
  }
}
2
likes
0
points
196
downloads

Publisher

unverified uploader

Weekly Downloads

Production-ready Flutter/Dart API client with auth refresh queue, exponential retry, smart caching and error tracking (Sentry-ready). Powered by Dio.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

crypto, dio, flutter, flutter_secure_storage, sentry_flutter

More

Packages that depend on apix