strapi_ldk 2.0.3 copy "strapi_ldk: ^2.0.3" to clipboard
strapi_ldk: ^2.0.3 copied to clipboard

A Flutter SDK (Lazy Development Kit / LDK) that wraps Strapi's REST & GraphQL APIs into a simple, type-safe, and intuitive client-side library.

Strapi LDK (Lazy Development Kit) #

pub package License: MIT Documentation

Tip

Official Documentation: Check out our comprehensive guides and API reference at https://lazy-development-kit.netlify.app

A comprehensive Flutter SDK that wraps Strapi's REST and GraphQL APIs into a simple, type-safe, and intuitive client-side library. Strapi LDK provides a Firebase-like experience while retaining Strapi's flexibility.

πŸš€ Phase 2 Now Available! #

Version 2.0 introduces powerful new features while maintaining full backward compatibility with Phase 1.

Features #

🌟 Phase 1 Features (Stable) #

  • πŸš€ Easy Setup: Initialize with just a few lines of code
  • πŸ” Authentication: JWT-based auth with automatic token management
  • πŸ“Š CRUD Operations: Intuitive interface for Create, Read, Update, Delete
  • πŸ” Advanced Querying: Fluent query builder with filters, sorting, and pagination
  • πŸ“ File Upload: Simple file and media upload with progress tracking
  • πŸ›‘οΈ Type Safety: Full TypeScript-like type safety with Dart
  • πŸ”„ Auto Retry: Built-in error handling and retry mechanisms
  • πŸ“± Cross Platform: Works on Android, iOS, Web, and Desktop

✨ Phase 2 Features (Advanced) #

  • πŸ”„ Real-time Updates: WebSocket-based live data synchronization
  • 🧠 Intelligent Caching: Multi-strategy caching (LRU, LFU, FIFO) with background refresh
  • πŸ“΄ Offline Support: Complete offline functionality with automatic sync
  • πŸ”’ Enhanced Security: Token refresh, biometric auth, security monitoring
  • ⚑ GraphQL Support: Full GraphQL client with subscriptions
  • 🎯 Optimistic Updates: Immediate UI updates with automatic rollback
  • πŸ” Global Search: Search across multiple collections simultaneously
  • πŸ’Ύ Transactions: Multi-operation atomic transactions
  • πŸ“Š Aggregations: Advanced data analysis and grouping
  • πŸ”” Event Streams: Real-time notifications for all major events

Getting Started #

Add LDK to your pubspec.yaml:

dependencies:
  strapi_ldk: ^2.0.3

Quick Start #

🎯 Phase 1 (Basic) - Stable API #

import 'package:strapi_ldk/strapi_ldk.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await LDK.initialize(
    baseUrl: 'https://your-strapi-app.com',
    enableLogging: true,
  );
  
  runApp(MyApp());
}

πŸš€ Phase 2 (Advanced) - New Features #

import 'package:strapi_ldk/strapi_ldk.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  await LDK.initialize(
    baseUrl: 'https://your-strapi-app.com',
    enableLogging: true,
    enableGraphQL: true,           // πŸ”„ Real-time subscriptions
    enableRealtime: true,          // ⚑ Live updates
    enableIntelligentCache: true,  // 🧠 Smart caching
    enableOfflineStorage: true,    // πŸ“΄ Offline support
    enableEnhancedAuth: true,      // πŸ”’ Advanced security
  );
  
  runApp(MyApp());
}

2. Authentication #

// Sign up a new user
try {
  final user = await LDK.authService.signUp(
    email: 'user@example.com',
    password: 'securePassword123',
    username: 'johndoe',
  );
  print('User created: ${user.email}');
} catch (e) {
  print('Sign up failed: $e');
}

// Sign in existing user
try {
  final user = await LDK.authService.signIn(
    email: 'user@example.com',
    password: 'securePassword123',
  );
  print('Signed in: ${user.email}');
} catch (e) {
  print('Sign in failed: $e');
}

// Listen to auth state changes
LDK.authService.authStateStream.listen((isAuthenticated) {
  if (isAuthenticated) {
    print('User is signed in');
  } else {
    print('User is signed out');
  }
});

// Sign out
await LDK.authService.signOut();

3. CRUD Operations #

// Get a collection reference
final posts = LDK.collection('posts');

// Create a new post
final newPost = await posts.create({
  'title': 'My First Post',
  'content': 'This is the content of my first post.',
  'status': 'published',
});

// Get all posts
final allPosts = await posts.get();
print('Found ${allPosts.data.length} posts');

// Get a specific post by ID
final post = await posts.getById(1);
print('Post title: ${post['title']}');

// Update a post
final updatedPost = await posts.update(1, {
  'title': 'Updated Post Title',
});

// Delete a post
await posts.delete(1);

4. Advanced Querying #

final posts = LDK.collection('posts');

// Complex query with filters, sorting, and pagination
final response = await posts
    .where('status', equals: 'published')
    .where('title', contains: 'Flutter')
    .where('createdAt', greaterThan: '2023-01-01')
    .sort('createdAt', descending: true)
    .populate('author')
    .populate('category')
    .paginate(page: 1, pageSize: 10)
    .get();

// Using query builder directly
final query = LDKQueryBuilder()
    .where('category', equals: 'technology')
    .or([
      LDKQueryBuilder().where('featured', equals: true),
      LDKQueryBuilder().where('views', greaterThan: 1000),
    ])
    .sort('publishedAt', descending: true)
    .limit(5);

final featuredPosts = await posts.get(query: query);

5. File Upload #

import 'dart:io';

// Upload a single file
final file = File('path/to/image.jpg');
try {
  final uploadedFile = await LDK.storageService.upload(
    file,
    alternativeText: 'Profile picture',
    caption: 'User profile image',
  );
  print('File uploaded: ${uploadedFile.url}');
} catch (e) {
  print('Upload failed: $e');
}

// Upload multiple files with progress tracking
final files = [File('image1.jpg'), File('image2.jpg')];
try {
  final uploadedFiles = await LDK.storageService.uploadMultiple(
    files,
    onProgress: (sent, total) {
      print('Progress: ${(sent / total * 100).toStringAsFixed(1)}%');
    },
  );
  print('Uploaded ${uploadedFiles.length} files');
} catch (e) {
  print('Upload failed: $e');
}

// Validate file before upload
final isValid = LDK.storageService.validateFile(
  file,
  maxSizeBytes: 5 * 1024 * 1024, // 5MB
  allowedMimeTypes: ['image/jpeg', 'image/png'],
  allowedExtensions: ['jpg', 'jpeg', 'png'],
);

if (isValid) {
  // Proceed with upload
}

πŸš€ Phase 2 Advanced Usage #

Real-time Updates #

// Subscribe to collection changes
final posts = LDK.enhancedCollection('posts');
posts.subscribeToUpdates().listen((event) {
  switch (event.mutation) {
    case MutationType.create:
      print('New post created: ${event.data}');
      break;
    case MutationType.update:
      print('Post updated: ${event.data}');
      break;
    case MutationType.delete:
      print('Post deleted');
      break;
  }
});

// Watch a query for live updates
posts.watchQuery(
  query: posts.where('status', equals: 'published').limit(10)
).listen((result) {
  updateUI(result.data);
});

Intelligent Caching #

// Automatic caching with smart strategies
final posts = LDK.enhancedCollection('posts');

// First call hits the network, subsequent calls use cache
final data1 = await posts.get(); // Network call
final data2 = await posts.get(); // Cache hit

// Force refresh from network
final freshData = await posts.get(forceRefresh: true);

// Cache statistics
LDK.cache.cacheStatsStream.listen((stats) {
  print('Cache hit ratio: ${(stats.hitRatio * 100).toStringAsFixed(1)}%');
});

Offline Support #

// Monitor online/offline status
LDK.subscribeToOfflineStatus().listen((isOnline) {
  if (isOnline) {
    showSuccessMessage('Back online! Syncing data...');
  } else {
    showWarningMessage('Offline mode - changes will sync later');
  }
});

// Operations work seamlessly offline
final post = await posts.create({
  'title': 'Offline Post',
  'content': 'This works offline too!',
}); // Queued for sync when online

// Monitor pending operations
LDK.offlineStorage.pendingOperationsStream.listen((operations) {
  print('${operations.length} operations pending sync');
});

Optimistic Updates #

// UI updates immediately, rolls back on error
try {
  final updatedPost = await posts.update(postId, {
    'title': 'New Title',
  }, optimistic: true);
  
  // UI already shows the new title
  showSuccess('Post updated!');
} catch (e) {
  // UI automatically reverted to original title
  showError('Update failed: $e');
}

Enhanced Security #

// Automatic token refresh
LDK.enhancedAuth.tokenRefreshStream.listen((isRefreshing) {
  if (isRefreshing) {
    showSpinner('Refreshing authentication...');
  }
});

// Enable biometric authentication
await LDK.enhancedAuth.enableBiometric(
  password: 'current_password',
);

// Sign in with biometrics
final user = await LDK.enhancedAuth.signInWithBiometric(
  email: 'user@example.com',
);

// Monitor security events
LDK.subscribeToSecurityEvents().listen((event) {
  if (event.type == SecurityEventType.suspiciousActivity) {
    showSecurityAlert('Suspicious activity detected!');
  }
});
// Search across multiple collections
final results = await LDK.globalSearch(
  'flutter tutorial',
  collections: ['posts', 'tutorials', 'documentation'],
  limit: 10,
);

// results = {
//   'posts': [...],
//   'tutorials': [...],
//   'documentation': [...]
// }

for (final entry in results.entries) {
  print('Found ${entry.value.length} results in ${entry.key}');
}

Transactions #

// Execute multiple operations atomically
final results = await LDK.executeTransaction([
  TransactionOperation(
    type: TransactionType.create,
    collection: 'posts',
    data: {'title': 'New Post', 'status': 'draft'},
  ),
  TransactionOperation(
    type: TransactionType.update,
    collection: 'users',
    entityId: userId,
    data: {'postCount': userPostCount + 1},
  ),
]);

print('Transaction completed: ${results.length} operations');

Aggregations #

// Advanced data analysis
final stats = await posts.aggregate(
  groupBy: ['category', 'status'],
  aggregates: {
    'views': AggregateFunction.sum,
    'id': AggregateFunction.count,
    'createdAt': AggregateFunction.min,
  },
  filters: posts.where('createdAt', greaterThan: '2023-01-01'),
);

print('Total views: ${stats['views']}');
print('Total posts: ${stats['count']}');

Custom Cache Strategies #

await LDK.initialize(
  baseUrl: 'https://your-strapi-app.com',
  cacheConfig: {
    'strategies': {
      'user': {
        'ttl': Duration(hours: 2).inMilliseconds,
        'maxSize': 50,
        'evictionPolicy': 'lru',
        'backgroundRefresh': true,
      },
      'posts': {
        'ttl': Duration(minutes: 15).inMilliseconds,
        'maxSize': 200,
        'evictionPolicy': 'lfu',
        'refreshOnAccess': true,
      },
    },
  },
);

Phase 1 Advanced Usage #

Custom Query Conditions #

final products = LDK.collection('products');

// Multiple filter conditions
final expensiveProducts = await products
    .where('price', greaterThanOrEqual: 100)
    .where('price', lessThan: 1000)
    .where('category', isIn: ['electronics', 'gadgets'])
    .where('inStock', equals: true)
    .get();

// Text search
final searchResults = await products
    .where('name', containsi: 'iphone') // Case-insensitive
    .where('description', contains: 'smartphone')
    .get();

// Null checks
final productsWithoutImages = await products
    .where('image', isNull: true)
    .get();

Error Handling #

try {
  final posts = await LDK.collection('posts').get();
} on LDKAuthException catch (e) {
  print('Authentication error: ${e.message}');
  // Redirect to login
} on LDKNetworkException catch (e) {
  print('Network error: ${e.message}');
  // Show retry option
} on LDKValidationException catch (e) {
  print('Validation error: ${e.message}');
  print('Field errors: ${e.errors}');
  // Show field-specific errors
} on LDKServerException catch (e) {
  print('Server error: ${e.message}');
  // Show generic error message
} catch (e) {
  print('Unexpected error: $e');
}

Working with Relations #

// Populate related data
final postsWithAuthors = await LDK.collection('posts')
    .populate('author')
    .populate('category')
    .populate('tags')
    .get();

// Deep population
final postsWithAuthorProfile = await LDK.collection('posts')
    .populate('author.profile')
    .get();

Configuration Options #

await LDK.initialize(
  baseUrl: 'https://your-strapi-app.com',
  authToken: 'optional-existing-jwt-token',
  enableLogging: true,
  connectTimeout: Duration(seconds: 30),
  receiveTimeout: Duration(seconds: 30),
);

Models #

LDK provides built-in models for common Strapi entities:

// User model
final user = LDKUser(
  id: 1,
  email: 'user@example.com',
  username: 'johndoe',
  confirmed: true,
  blocked: false,
);

// File model
final file = StrapiFile(
  id: 1,
  name: 'image.jpg',
  url: 'https://example.com/uploads/image.jpg',
  mime: 'image/jpeg',
  size: 1024.0,
  // ... other properties
);

Testing #

LDK is thoroughly tested. To run tests:

flutter test

Contributing #

Contributions are welcome! Please read our contributing guide for details on our code of conduct and the process for submitting pull requests.

License #

This project is licensed under the MIT License - see the LICENSE file for details.

Support #

Roadmap #

  • ❌ GraphQL support
  • ❌ Real-time subscriptions
  • ❌ Offline caching
  • ❌ CLI for code generation
  • ❌ Advanced caching strategies

Made with ❀️ for the Flutter and Strapi communities.

2
likes
130
points
13
downloads

Documentation

API reference

Publisher

verified publisherlazytechsavvy.co.in

Weekly Downloads

A Flutter SDK (Lazy Development Kit / LDK) that wraps Strapi's REST & GraphQL APIs into a simple, type-safe, and intuitive client-side library.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

connectivity_plus, crypto, dio, flutter, flutter_secure_storage, gql, gql_http_link, gql_link, graphql, hive, hive_flutter, json_annotation, logger, meta, mime, rxdart, web_socket_channel

More

Packages that depend on strapi_ldk