strapi_ldk 2.0.3
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) #
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!');
}
});
Global Search #
// 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 #
- π Official Documentation
- π Issue Tracker
- π¬ Discussions
Roadmap #
- β GraphQL support
- β Real-time subscriptions
- β Offline caching
- β CLI for code generation
- β Advanced caching strategies
Made with β€οΈ for the Flutter and Strapi communities.