fairu_sdk_dart 0.1.0 copy "fairu_sdk_dart: ^0.1.0" to clipboard
fairu_sdk_dart: ^0.1.0 copied to clipboard

Official Dart/Flutter SDK for the Fairu Digital Asset Management API. Provides type-safe GraphQL queries, mutations, uploads, and image transformations.

Fairu SDK for Dart/Flutter #

Official Dart/Flutter SDK for the Fairu Digital Asset Management API.

Features #

  • Asset Management - Query, update, delete, block/unblock files
  • Folder Management - Create, update, delete, move folders
  • Gallery Management - Create and manage galleries
  • Copyright & License Management - Full CRUD operations
  • File Uploads - Simple and multipart uploads with progress tracking
  • FileProxy URL Builder - Fluent API for image transformations (resize, crop, format conversion)
  • Type-safe - Full type definitions for all operations
  • Error Handling - Structured error classes for validation, authentication, and GraphQL errors

Installation #

Add this to your pubspec.yaml:

dependencies:
  fairu_sdk_dart:
    git:
      url: https://github.com/fairu-media/fairu-sdk-dart.git

Or, if published to pub.dev:

dependencies:
  fairu_sdk_dart: ^0.1.0

Then run:

dart pub get

Quick Start #

import 'package:fairu_sdk_dart/fairu_sdk_dart.dart';

void main() async {
  // Initialize the SDK
  final fairu = FairuSdk(
    config: FairuClientConfig(
      token: 'your-api-token',
    ),
  );

  // Fetch an asset
  final asset = await fairu.assets.find('asset-id');
  print('Asset: ${asset?['name']}');

  // List assets
  final assets = await fairu.assets.list(page: 1, perPage: 10);
  print('Total assets: ${assets.paginatorInfo.total}');
}

Configuration #

final config = FairuClientConfig(
  // GraphQL API URL (default: https://fairu.app/graphql)
  url: 'https://fairu.app/graphql',

  // Static API token
  token: 'your-api-token',

  // Or use a dynamic token provider
  getToken: () async => await getTokenFromSecureStorage(),

  // FileProxy base URL (default: https://files.fairu.app)
  fileProxyUrl: 'https://files.fairu.app',

  // Request timeout in milliseconds (default: 30000)
  timeout: 30000,
);

final fairu = FairuSdk(config: config);

Assets (Files) #

Find Asset #

// By ID
final asset = await fairu.assets.find('asset-id');

// By path
final asset = await fairu.assets.findByPath('/marketing/hero.jpg');

List Assets #

final result = await fairu.assets.list(
  page: 1,
  perPage: 20,
  folderId: 'folder-id', // optional
);

print('Total: ${result.paginatorInfo.total}');
for (final asset in result.data) {
  print('- ${asset['name']}');
}

Search Assets #

final result = await fairu.assets.search(
  searchTerm: 'hero image',
  page: 1,
  perPage: 20,
  orderBy: 'created_at',
  orderDirection: 'DESC',
);

Update Asset #

final updated = await fairu.assets.update(
  id: 'asset-id',
  alt: 'New alt text',
  caption: 'New caption',
  description: 'New description',
  focalPoint: '50-50-1',
  copyrightIds: ['copyright-id'],
  licenseIds: ['license-id'],
);

Delete Asset #

final success = await fairu.assets.delete('asset-id');

Block/Unblock Asset #

await fairu.assets.block('asset-id');
await fairu.assets.unblock('asset-id');

Rename/Move Asset #

// Rename
await fairu.assets.rename('asset-id', 'new-name');

// Move to different folder
await fairu.assets.move('asset-id', parentId: 'folder-id');

// Move to root
await fairu.assets.move('asset-id');

Folders #

List Folder Contents #

final result = await fairu.folders.getContent(
  folderId: 'folder-id', // null for root
  page: 1,
  perPage: 20,
  search: 'optional search term',
  orderBy: 'name',
  orderDirection: 'ASC',
);

Find Folder by Path #

final folder = await fairu.folders.findByPath('/marketing/2024');

Create Folder #

final folder = await fairu.folders.create(
  name: 'New Folder',
  parentId: 'parent-folder-id', // optional
  autoAssignCopyright: true, // optional
  copyrightIds: ['copyright-id'], // optional
);

Update/Delete Folder #

await fairu.folders.update(id: 'folder-id', name: 'Renamed Folder');
await fairu.folders.delete('folder-id');

File Uploads #

Simple Upload #

import 'dart:io';

final file = File('/path/to/image.jpg');

final result = await fairu.upload.simple(
  file: file,
  filename: 'image.jpg',
  folderId: 'folder-id',
  alt: 'Alt text',
  caption: 'Caption',
  description: 'Description',
  onProgress: (progress) {
    print('Upload: ${progress.percentage}%');
  },
  onStatusChange: (status) {
    print('Status: $status'); // preparing, uploading, syncing, completed
  },
);

print('Uploaded file ID: ${result.id}');

Multipart Upload (Large Files) #

import 'dart:io';

final file = File('/path/to/large-video.mp4');
final fileSize = await file.length();

// Initialize multipart upload
final session = await fairu.upload.initMultipart(
  filename: 'large-video.mp4',
  fileSize: fileSize,
  folderId: 'folder-id',
  contentType: 'video/mp4',
);

// Upload parts
final fileBytes = await file.readAsBytes();
final partSize = session.partSize;
int offset = 0;
int partNumber = 1;

while (offset < fileBytes.length) {
  final end = (offset + partSize < fileBytes.length)
      ? offset + partSize
      : fileBytes.length;
  final partData = fileBytes.sublist(offset, end);

  // Get presigned URL for this part (if not provided upfront)
  final part = session.parts.isNotEmpty
      ? session.parts[partNumber - 1]
      : await session.getPartUrl(partNumber);

  // Upload the part
  await session.uploadPart(part, partData);

  offset = end;
  partNumber++;
}

// Complete the upload
final result = await session.complete();
print('Upload complete: ${result.id}');

FileProxy URL Builder #

Build optimized image URLs with transformations:

// Basic usage
final url = fileProxy('asset-id', 'image.jpg')
  .width(800)
  .height(600)
  .quality(85)
  .format(ImageFormat.webp)
  .build();

// Result: https://files.fairu.app/asset-id/image.jpg?width=800&height=600&quality=85&format=webp

Available Transformations #

final url = fileProxy('asset-id', 'image.jpg')
  // Dimensions (1-6000 pixels)
  .width(800)
  .height(600)
  .dimensions(800, 600) // shorthand

  // Quality (1-100, default: 95)
  .quality(85)

  // Format
  .format(ImageFormat.webp) // jpg, png, webp

  // Fit mode
  .fit(FitMode.cover) // cover, contain

  // Focal point for smart cropping
  .focal(50, 30, zoom: 1.5) // x, y, zoom
  .focalFromString('50-30-1.5') // from string

  // Return original file
  .raw(true)

  // Convert SVG to raster
  .processSvg(true)

  // Video frame extraction
  .timestamp('00:05:30')
  .timestampFromSeconds(330)

  // Video version
  .version(VideoVersion.high) // low, medium, high

  // Signed URL for protected files
  .sign('your-secret-key')
  .withSignature('pre-computed-sig', '1234567890')

  .build();

Utility Functions #

// Get image metadata URL
final metaUrl = FileProxyUtils.meta('asset-id');

// HLS video streaming
final hlsUrl = FileProxyUtils.hls('tenant-id', 'asset-id', 'playlist.m3u8');

// HLS decryption key
final keyUrl = FileProxyUtils.hlsKey('asset-id', VideoVersion.high, 'key');

Galleries #

// Find gallery
final gallery = await fairu.galleries.find('gallery-id');

// List galleries
final result = await fairu.galleries.list(
  tenantIds: ['tenant-id'],
  page: 1,
  perPage: 20,
);

// Create gallery
final gallery = await fairu.galleries.create(
  name: 'Summer 2024',
  folderId: 'folder-id',
  description: 'Photos from summer vacation',
  location: 'Beach',
  date: DateTime(2024, 7, 15),
  active: true,
);

// Create share link
final shareUrl = await fairu.galleries.createShareLink('gallery-id');

Copyrights & Licenses #

Copyrights #

// List copyrights
final result = await fairu.copyrights.list(page: 1, perPage: 20);

// Create copyright
final copyright = await fairu.copyrights.create(
  name: 'Photographer Name',
  email: 'photo@example.com',
  website: 'https://example.com',
);

// Update copyright
await fairu.copyrights.update(id: 'id', name: 'Updated Name');

// Delete copyright
await fairu.copyrights.delete('id', deleteAssets: false, deleteLicenses: false);

Licenses #

// List licenses
final result = await fairu.licenses.list(page: 1, perPage: 20);

// Create license
final license = await fairu.licenses.create(
  name: 'Standard License',
  copyrightId: 'copyright-id',
  type: 'STANDARD', // or 'PERIOD'
  active: true,
);

Tenant Operations #

// Get tenant info
final tenant = await fairu.tenant.get();

// Update tenant settings
await fairu.tenant.update(
  useAi: true,
  forceFileAlt: true,
);

// Health check
final health = await fairu.tenant.healthCheck();
print('API Version: ${health?['version']}');
print('Healthy: ${health?['healthy']}');

Error Handling #

The SDK provides structured error classes:

try {
  await fairu.assets.find('non-existent-id');
} on ValidationError catch (e) {
  // Handle validation errors
  print('Validation errors:');
  for (final field in e.fields) {
    print('  $field: ${e.getFieldErrors(field)}');
  }
} on AuthenticationError catch (e) {
  // Handle auth errors
  if (e.isTokenExpired) {
    // Refresh token
  }
} on NetworkError catch (e) {
  // Handle network errors
  print('Network error: ${e.message}');
} on FairuError catch (e) {
  // Handle other errors
  print('Error: ${e.message}');
}

Raw GraphQL Queries #

For operations not covered by the SDK:

// Query
final result = await fairu.query('''
  query CustomQuery(\$id: ID!) {
    fairuFile(id: \$id) {
      id
      name
      customField
    }
  }
''', variables: {'id': 'asset-id'});

// Mutation
final result = await fairu.mutate('''
  mutation CustomMutation(\$id: ID!) {
    someCustomMutation(id: \$id) {
      success
    }
  }
''', variables: {'id': 'some-id'});

License #

MIT License - see LICENSE file for details.

0
likes
150
points
104
downloads

Publisher

verified publisherfairu.app

Weekly Downloads

Official Dart/Flutter SDK for the Fairu Digital Asset Management API. Provides type-safe GraphQL queries, mutations, uploads, and image transformations.

Homepage
Repository (GitHub)

Documentation

API reference

License

MIT (license)

Dependencies

crypto, graphql, http, meta

More

Packages that depend on fairu_sdk_dart