luqta_sdk 1.3.3
luqta_sdk: ^1.3.3 copied to clipboard
Official Luqta Flutter SDK for contests, quizzes, rewards & gamification. Dual-mode support (auto-rendered UI or custom API). 95% Web SDK parity. Production-ready.
Luqta Flutter SDK #
Official Flutter SDK for the Luqta API - Integrate contests, quizzes, rewards, and gamification features into your Flutter applications.
Version: 1.3.3
Table of Contents #
- Features
- Installation
- Quick Start
- Usage Modes
- API Reference
- Available Widgets
- Theming & Branding
- Localization
- Error Handling
- Support
Features #
- Two Integration Modes: Preconfigured (instant UI) or Custom (full control)
- Contest Management: Browse, participate, and track contest progress
- Level Completion: Text, QR, Link, Image, and Quiz levels
- Rewards & Gamification: Points, rewards, and achievements
- Beautiful UI: Pre-built screens and widgets
- Multi-language: English and Arabic support
- RTL Support: Right-to-left layout for Arabic
- Secure: Rate limiting, request deduplication, token management
Installation #
Step 1: Add dependency #
Add to your pubspec.yaml:
dependencies:
luqta_sdk: ^1.3.3
Step 2: Install #
flutter pub get
Step 3: Import #
import 'package:luqta_sdk/luqta_sdk.dart';
Quick Start #
Minimum Setup (3 Steps) #
// Step 1: Create client
final client = LuqtaClient(
config: LuqtaConfig(
apiKey: 'your-api-key',
appId: 'your-app-id',
),
);
// Step 2: Initialize SDK
await client.initializeSdk();
// Step 3: Initialize user
await client.syncAndInitializeUser(UserProfile(
name: 'John Doe',
email: 'john@example.com',
policyAccept: true,
));
// Done! Now use the SDK
Usage Modes #
The SDK supports two modes - choose based on your needs:
| Mode | Best For | Effort |
|---|---|---|
| Preconfigured | Quick integration, standard UI | Minimal |
| Custom | Full UI control, custom designs | More work |
1. Preconfigured Mode (Easiest) - Complete UI in One Line #
Use this mode to get a complete, beautiful UI with minimal code. The SDK handles everything: screens, navigation, API calls, and state management.
Complete Example #
import 'package:flutter/material.dart';
import 'package:luqta_sdk/luqta_sdk.dart';
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
LuqtaClient? _client;
bool _isLoading = true;
@override
void initState() {
super.initState();
_initializeLuqta();
}
Future<void> _initializeLuqta() async {
// Create client with preconfigured mode
_client = LuqtaClient(
config: LuqtaConfig(
mode: LuqtaMode.preconfigured, // Enable auto UI
apiKey: 'your-api-key',
appId: 'your-app-id',
production: false, // true for production
locale: 'en', // 'en' or 'ar'
rtl: false, // true for Arabic
branding: BrandingConfig(
primaryColor: Color(0xFF5304FB),
// Optional: customize more colors
),
onAction: (action) {
print('Action: ${action['type']}');
},
onError: (error) {
print('Error: $error');
},
),
);
// Initialize SDK
await _client!.initializeSdk();
// Initialize user
await _client!.syncAndInitializeUser(UserProfile(
name: 'John Doe',
email: 'john@example.com',
policyAccept: true,
));
setState(() => _isLoading = false);
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return Center(child: CircularProgressIndicator());
}
// Just call render() - that's it!
return _client!.render();
}
}
What You Get #
The render() method provides:
- Contest list with carousel/grid views
- Contest detail pages with levels
- Level completion flows (text, QR, link, image)
- Progress tracking
- Congratulations screens
- Private contest access code handling
- All navigation and animations
2. Custom Mode (Full Control) - Build Your Own UI #
Use this mode when you need complete control over the UI design. Use SDK APIs for data and optionally use individual widgets.
Basic Setup #
import 'package:luqta_sdk/luqta_sdk.dart';
// Create client (custom mode is default)
final client = LuqtaClient(
config: LuqtaConfig(
mode: LuqtaMode.custom,
apiKey: 'your-api-key',
appId: 'your-app-id',
),
);
// Initialize
await client.initializeSdk();
await client.syncAndInitializeUser(UserProfile(
name: 'John Doe',
email: 'john@example.com',
policyAccept: true,
));
Using API Services #
// Get all contests
final contestsResponse = await client.contests.getAll();
final contests = contestsResponse.data.items;
// Get contest details
final contest = await client.contests.getById(123);
// Participate in contest
await client.contests.participate(123);
// Participate with access code (private contest)
await client.contests.participate(123, accessCode: 'ABC123');
// Complete a level
await client.levels.complete(456, data: {'textContent': 'answer'});
// Get user rewards
final rewards = await client.rewards.getAll();
// Get notifications
final notifications = await client.notifications.getAll();
Using Individual Widgets #
You can use SDK widgets in your custom UI:
// Contest Card
ContestCard(
contest: contest,
locale: 'en',
onTap: () => navigateToDetail(contest),
)
// Contests Screen
ContestsScreen(
locale: 'en',
onFetchContests: ({required int page, required int limit}) async {
final response = await client.contests.getAll();
return response.data.items.map((e) => Contest.fromJson(e)).toList();
},
onContestTap: (contest) {
// Navigate to contest detail
},
)
// Contest Detail Screen
ContestDetailScreen(
contest: contest,
locale: 'en',
onFetchContestDetails: (contestId) async {
final response = await client.getContestDetailsProgress(contestId);
return Contest.fromJson(response['data']);
},
onJoinContest: (contestId, accessCode) async {
await client.contests.participate(contestId, accessCode: accessCode);
// Return updated contest
},
onCompleteLevel: (levelId, levelType, {textContent, qrCode, link, imageUrl}) async {
final response = await client.levels.complete(levelId, data: {
'textContent': textContent,
'qrCode': qrCode,
'link': link,
'imageUrl': imageUrl,
});
return response['success'] == true;
},
onBack: () => Navigator.pop(context),
)
Building Fully Custom UI #
class MyCustomContestsPage extends StatefulWidget {
@override
State<MyCustomContestsPage> createState() => _MyCustomContestsPageState();
}
class _MyCustomContestsPageState extends State<MyCustomContestsPage> {
List<Contest> _contests = [];
bool _isLoading = true;
@override
void initState() {
super.initState();
_loadContests();
}
Future<void> _loadContests() async {
final response = await client.contests.getAll();
setState(() {
_contests = response.data.items.map((e) => Contest.fromJson(e)).toList();
_isLoading = false;
});
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return Center(child: CircularProgressIndicator());
}
return ListView.builder(
itemCount: _contests.length,
itemBuilder: (context, index) {
final contest = _contests[index];
// Use SDK widget
return ContestCard(
contest: contest,
locale: 'en',
onTap: () => _openContest(contest),
);
// Or build your own card
// return MyCustomContestCard(contest: contest);
},
);
}
}
API Reference #
Client Methods #
| Method | Description |
|---|---|
initializeSdk() |
Initialize SDK with API key |
initializeUser() |
Initialize authenticated user |
syncUser(userData) |
Sync user profile to backend |
syncAndInitializeUser(userData) |
Sync and initialize in one call |
isInitialized() |
Check if user is initialized |
render() |
Get auto-rendered UI (preconfigured mode) |
API Services #
// Contests
client.contests.getAll()
client.contests.getById(id)
client.contests.participate(id, {accessCode})
client.contests.compete(id)
// Levels
client.levels.getByContest(contestId)
client.levels.complete(levelId, {data})
client.levels.completeWithImage(levelId, imageBase64)
// Quiz
client.quiz.getByLevel(levelId)
client.quiz.submit(quizId, answers)
// Rewards
client.rewards.getAll()
client.rewards.redeem(rewardId)
// Notifications
client.notifications.getAll()
client.notifications.markAsRead(notificationId)
// Profile
client.profile.get()
client.profile.update(data)
Available Widgets #
Screens #
| Widget | Description |
|---|---|
ContestsScreen |
Full contests list (horizontal/vertical) |
ContestDetailScreen |
Contest detail with levels |
Widgets #
| Widget | Description |
|---|---|
ContestCard |
Contest card for lists |
LevelItem |
Level item in list |
QuizWidget |
Quiz display and submission |
CongratulationDialog |
Success celebration |
LuqtaToast |
Toast notifications |
Level Completion Widgets #
| Widget | Description |
|---|---|
TextLevelCompletion |
Text input level |
QrLevelCompletion |
QR code scanner level |
LinkLevelCompletion |
Link visit level |
ImageLevelCompletion |
Image upload level |
Theming & Branding #
Using BrandingConfig #
LuqtaConfig(
branding: BrandingConfig(
primaryColor: Color(0xFF5304FB),
secondaryColor: Color(0xFF6366F1),
backgroundColor: Colors.white,
textColor: Colors.black87,
// Add more customization
),
)
Using LuqtaTheme #
final theme = LuqtaTheme.generateTheme(
branding: BrandingConfig(
primaryColor: Color(0xFF5304FB),
),
);
MaterialApp(
theme: theme,
// ...
)
Localization #
Set Locale #
LuqtaConfig(
locale: 'ar', // 'en' or 'ar'
rtl: true, // Enable RTL for Arabic
)
Available Translations #
// Use translations in custom UI
final text = LuqtaTranslations.translate('contests.title', locale: 'en');
// Returns: "Contests"
final textAr = LuqtaTranslations.translate('contests.title', locale: 'ar');
// Returns: "المسابقات"
Error Handling #
Using Try-Catch #
try {
await client.contests.participate(contestId);
} on LuqtaError catch (e) {
print('Error: ${e.message}');
print('Code: ${e.code}');
print('Status: ${e.status}');
} catch (e) {
print('Unknown error: $e');
}
Using Callbacks #
LuqtaConfig(
onError: (error) {
if (error is LuqtaError) {
showToast(error.message);
}
},
)
Common Error Codes #
| Code | Description |
|---|---|
MISSING_API_KEY |
API key not provided |
MISSING_APP_ID |
App ID not provided |
INVALID_EMAIL_FORMAT |
Invalid email format |
INVALID_PHONE_FORMAT |
Invalid phone format |
RATE_LIMIT_EXCEEDED |
Too many requests |
REQUEST_FAILED |
API request failed |
Complete Examples #
Example 1: Simple Integration (Preconfigured) #
import 'package:flutter/material.dart';
import 'package:luqta_sdk/luqta_sdk.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: LuqtaPage(),
);
}
}
class LuqtaPage extends StatefulWidget {
@override
State<LuqtaPage> createState() => _LuqtaPageState();
}
class _LuqtaPageState extends State<LuqtaPage> {
LuqtaClient? _client;
bool _ready = false;
@override
void initState() {
super.initState();
_init();
}
Future<void> _init() async {
_client = LuqtaClient(
config: LuqtaConfig(
mode: LuqtaMode.preconfigured,
apiKey: 'YOUR_API_KEY',
appId: 'YOUR_APP_ID',
),
);
await _client!.initializeSdk();
await _client!.syncAndInitializeUser(UserProfile(
email: 'user@example.com',
policyAccept: true,
));
setState(() => _ready = true);
}
@override
Widget build(BuildContext context) {
if (!_ready) return Scaffold(body: Center(child: CircularProgressIndicator()));
return Scaffold(body: _client!.render());
}
}
Example 2: Embed in Existing App #
class HomePage extends StatelessWidget {
final LuqtaClient client;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('My App')),
body: Column(
children: [
// Your custom header
MyHeader(),
// Luqta contests section
SizedBox(
height: 300,
child: client.render(),
),
// Your other content
MyOtherContent(),
],
),
);
}
}
Example 3: Custom UI with SDK Widgets #
class CustomContestsPage extends StatelessWidget {
final LuqtaClient client;
@override
Widget build(BuildContext context) {
return ContestsScreen(
locale: 'en',
showSeeAllButton: true,
onFetchContests: ({required int page, required int limit}) async {
final response = await client.contests.getAll();
return response.data.items.map((e) => Contest.fromJson(e)).toList();
},
onContestTap: (contest) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ContestDetailScreen(
contest: contest,
locale: 'en',
onBack: () => Navigator.pop(context),
onFetchContestDetails: (id) async {
final response = await client.getContestDetailsProgress(id);
return Contest.fromJson(response['data']);
},
onCompleteLevel: (levelId, type, {textContent, qrCode, link, imageUrl}) async {
final response = await client.levels.complete(levelId, data: {
'textContent': textContent,
});
return response['success'] == true;
},
),
),
);
},
);
}
}
Support #
- GitHub Issues: Report bugs
- Email: support@luqta.com
- Documentation: Full API Docs
License #
MIT License - See LICENSE file for details.