luqta_sdk 1.3.1 copy "luqta_sdk: ^1.3.1" to clipboard
luqta_sdk: ^1.3.1 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.0

Table of Contents #


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.0

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, imagePath)

// 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 #


License #

MIT License - See LICENSE file for details.

0
likes
0
points
36
downloads

Documentation

Documentation

Publisher

unverified uploader

Weekly Downloads

Official Luqta Flutter SDK for contests, quizzes, rewards & gamification. Dual-mode support (auto-rendered UI or custom API). 95% Web SDK parity. Production-ready.

Homepage
Repository (GitHub)
View/report issues

Topics

#gamification #contests #quizzes #rewards #engagement

License

unknown (license)

Dependencies

cached_network_image, flutter, flutter_secure_storage, http, image_picker, intl, mobile_scanner, provider, shared_preferences, shimmer, url_launcher

More

Packages that depend on luqta_sdk