las_app 1.1.1+35 copy "las_app: ^1.1.1+35" to clipboard
las_app: ^1.1.1+35 copied to clipboard

The official SLiQ SDK for Loan Against Mutual Funds and Securities integration.

example/lib/main.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:las_app/lamf_sdk.dart'; // From the local package

void main() {
  runApp(const HostApp());
}

// Create an easy to customize configuration object for the example app
class SdkConfig {
  static const Color primaryOrange = Color(0xFFFF6600);
  static const Color darkBlue = Color(0xFF2C3178);
  static const Color scaffoldBackgroundLight = Color(0xFFF5F7FA);

  static ThemeData get lightTheme => ThemeData(
    brightness: Brightness.light,
    primaryColor: primaryOrange,
    colorScheme: ColorScheme.fromSeed(
      seedColor: primaryOrange,
      brightness: Brightness.light,
    ),
    scaffoldBackgroundColor: scaffoldBackgroundLight,
    appBarTheme: const AppBarTheme(
      backgroundColor: Colors.white,
      foregroundColor: darkBlue,
      elevation: 1,
      centerTitle: true,
      titleTextStyle: TextStyle(
        color: darkBlue,
        fontSize: 22,
        fontWeight: FontWeight.w700,
      ),
    ),
    elevatedButtonTheme: ElevatedButtonThemeData(
      style: ElevatedButton.styleFrom(
        backgroundColor: primaryOrange,
        foregroundColor: Colors.white,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
      ),
    ),
  );
}

class HostApp extends StatelessWidget {
  const HostApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'SLiQ Fin Demo',
      theme: SdkConfig.lightTheme,
      home: const HostHomePage(),
      debugShowCheckedModeBanner: false,
    );
  }
}

class HostHomePage extends StatefulWidget {
  const HostHomePage({super.key});

  @override
  State<HostHomePage> createState() => _HostHomePageState();
}

class _HostHomePageState extends State<HostHomePage> {
  final TextEditingController _phoneController = TextEditingController(
    text: "",
  );
  final TextEditingController _clientIdController = TextEditingController(
    text: "VALUENABLE_DEV_001",
  );
  final TextEditingController _clientSecretController = TextEditingController(
    text: "VE_dev_92KxPq71LmZa8RtQ",
  );

  bool _useCurrentTime = true;
  Color _selectedThemeColor = const Color(0xFFFF6600);

  bool _allowLAS = false;
  bool _allowLAMF = false;
  bool _allowLAIP = false;

  final List<Color> _themeColors = [
    const Color(0xFFFF6600), // SLiQ Orange
    const Color(0xFF2C3178), // SLiQ Dark Blue
    Colors.deepPurple,
    Colors.green,
    Colors.black,
  ];

  @override
  void dispose() {
    _phoneController.dispose();
    _clientIdController.dispose();
    _clientSecretController.dispose();
    super.dispose();
  }

  Future<void> _fetchTokenAndLaunch() async {
    final mobile = _phoneController.text.trim();
    final clientId = _clientIdController.text.trim();
    final clientSecret = _clientSecretController.text.trim();

    if (mobile.isEmpty || clientId.isEmpty || clientSecret.isEmpty) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Please fill all required fields')),
      );
      return;
    }

    // Mobile Number Validation
    final isValidMobile =
        mobile.length == 10 && RegExp(r'^\d+$').hasMatch(mobile);

    if (!isValidMobile) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Please enter a valid 10-digit mobile number'),
        ),
      );
      return;
    }

    final verificationTime = _useCurrentTime
        ? DateTime.now().toIso8601String()
        : "";

    showDialog(
      context: context,
      barrierDismissible: false,
      builder: (_) => const Center(child: CircularProgressIndicator()),
    );

    const v = String.fromEnvironment('Flavor');
    var url = v == 'dev'
        ? 'api-dev'
        : v == 'uat'
        ? 'api-uat'
        : 'api';

    try {
      final response = await http.post(
        Uri.parse(
          'https://$url.valuenable.in/lamf/customer/generate-sdk-token',
        ),
        headers: {
          'Content-Type': 'application/json',
          'User-Agent':
              'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36',
        },
        body: jsonEncode({
          "clientId": clientId,
          "clientSecret": clientSecret,
          "phoneNumber": "+91$mobile",
          "verificationTime": verificationTime,
        }),
      );

      Navigator.pop(context); // close loader

      if (response.statusCode == 200 || response.statusCode == 201) {
        final data = jsonDecode(response.body);

        debugPrint("Generate SDK Token Response: $data");

        // Extract token safely
        String? token = data['token'];
        if (token == null && data['data'] != null && data['data'] is Map) {
          token = data['data']['token'];
        }

        // Extract otpRef safely
        String? otpRef = data['otp_ref'] ?? data['otpRef'];
        if (otpRef == null && data['data'] != null && data['data'] is Map) {
          otpRef = data['data']['otp_ref'] ?? data['data']['otpRef'];
        }

        String? reqId;
        if (data['req_id'] != null &&
            data['req_id'] is List &&
            data['req_id'].isNotEmpty) {
          reqId = data['req_id'][0];
        } else if (data['data'] != null &&
            data['data']['req_id'] is List &&
            data['data']['req_id'].isNotEmpty) {
          reqId = data['data']['req_id'][0];
        }

        String? sharesReqId;
        if (data['shares_req_id'] != null &&
            data['shares_req_id'] is List &&
            data['shares_req_id'].isNotEmpty) {
          sharesReqId = data['shares_req_id'][0];
        } else if (data['data'] != null &&
            data['data']['shares_req_id'] is List &&
            data['data']['shares_req_id'].isNotEmpty) {
          sharesReqId = data['data']['shares_req_id'][0];
        }

        debugPrint("Extracted Token: $token");
        debugPrint("Extracted OTP Ref: $otpRef");
        debugPrint("Extracted Req ID: $reqId");
        debugPrint("Extracted Shares Req ID: $sharesReqId");

        debugPrint(
          "DEBUG Launching SDK -> reqId: $reqId, sharesReqId: $sharesReqId, token: $token",
        );

        final customTheme = ThemeData(
          primaryColor: _selectedThemeColor,
          colorScheme: ColorScheme.fromSeed(seedColor: _selectedThemeColor),
        );

        // Pass the 10-digit mobile number directly
        final sdkMobile = mobile;

        List<SdkJourney> allowedJourneys = [];
        if (_allowLAS) allowedJourneys.add(SdkJourney.LAS);
        if (_allowLAMF) allowedJourneys.add(SdkJourney.LAMF);
        if (_allowLAIP) allowedJourneys.add(SdkJourney.LAIP);

        LamfSdk.launch(
          context,
          mobileNumber: sdkMobile,
          token: token,
          otpRef: otpRef,
          reqId: reqId,
          sharesReqId: sharesReqId,
          theme: customTheme,
          redirectUrl: 'https://my-custom-host-app-redirect.com/success',
          allowedJourneys: allowedJourneys,
          environment: 'uat',
        );
      } else {
        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text(
              'Service is currently unavailable. Please try again later.',
            ),
          ),
        );
      }
    } catch (e) {
      Navigator.pop(context); // close loader
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text(
            'Unable to connect to the server. Please check your internet connection.',
          ),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF8F9FA),
      appBar: AppBar(
        backgroundColor: Colors.white,
        elevation: 0,
        centerTitle: false,
        title: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Text(
              'SLiQ',
              style: TextStyle(
                fontWeight: FontWeight.w900,
                fontSize: 24,
                color: Color(0xFFFF6600),
              ),
            ),
            const Text(
              'FIN',
              style: TextStyle(
                fontWeight: FontWeight.w900,
                fontSize: 24,
                color: Color(0xFF2C3178),
              ),
            ),
            const SizedBox(width: 12),
            Container(
              padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
              decoration: const BoxDecoration(
                color: Color(0xFFE2E8F0),
                borderRadius: BorderRadius.all(Radius.circular(6)),
              ),
              child: const Text(
                'SDK Demo',
                style: TextStyle(
                  fontWeight: FontWeight.w600,
                  fontSize: 12,
                  color: Color(0xFF475569),
                ),
              ),
            ),
          ],
        ),
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(24.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            Container(
              padding: const EdgeInsets.all(24),
              decoration: BoxDecoration(
                gradient: const LinearGradient(
                  colors: [Color(0xFF2C3178), Color(0xFF1E2255)],
                  // Deep blue gradient
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                ),
                borderRadius: BorderRadius.circular(16),
                boxShadow: [
                  BoxShadow(
                    color: const Color(0xFF2C3178).withOpacity(0.3),
                    blurRadius: 15,
                    offset: const Offset(0, 8),
                  ),
                ],
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Container(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 10,
                      vertical: 4,
                    ),
                    decoration: BoxDecoration(
                      color: const Color(0xFFFF6600),
                      borderRadius: BorderRadius.circular(20),
                    ),
                    child: const Text(
                      'INTEGRATION',
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 10,
                        fontWeight: FontWeight.bold,
                        letterSpacing: 1,
                      ),
                    ),
                  ),
                  const SizedBox(height: 16),
                  const Text(
                    'Instant Loans Against\nInsurance, Mutual Funds & Shares',
                    style: TextStyle(
                      fontSize: 22,
                      fontWeight: FontWeight.bold,
                      color: Colors.white,
                      height: 1.3,
                    ),
                  ),
                  const SizedBox(height: 12),
                  const Text(
                    'Fast approvals, secure processing, and smart liquidity when you need it.',
                    style: TextStyle(
                      fontSize: 15,
                      color: Colors.white70,
                      height: 1.4,
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 32),

            const Text(
              'Get Started',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
                color: Color(0xFF2C3178),
              ),
            ),
            const SizedBox(height: 16),

            Container(
              padding: const EdgeInsets.all(20),
              decoration: BoxDecoration(
                color: Colors.white,
                borderRadius: BorderRadius.circular(12),
                boxShadow: [
                  BoxShadow(
                    color: Colors.black.withOpacity(0.05),
                    blurRadius: 10,
                    offset: const Offset(0, 5),
                  ),
                ],
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  TextField(
                    controller: _phoneController,
                    keyboardType: TextInputType.phone,
                    inputFormatters: [
                      FilteringTextInputFormatter.digitsOnly,
                      LengthLimitingTextInputFormatter(10),
                    ],
                    decoration: InputDecoration(
                      labelText: 'Mobile Number',
                      hintText: 'Enter 10 digits',
                      prefixText: '+91 ',
                      prefixStyle: const TextStyle(
                        color: Colors.black87,
                        fontSize: 16,
                      ),
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(8),
                      ),
                      prefixIcon: const Icon(
                        Icons.phone,
                        color: Color(0xFFFF6600),
                      ),
                      focusedBorder: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(8),
                        borderSide: const BorderSide(
                          color: Color(0xFFFF6600),
                          width: 2,
                        ),
                      ),
                    ),
                  ),
                  const SizedBox(height: 20),
                  ElevatedButton(
                    style: ElevatedButton.styleFrom(
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      backgroundColor: const Color(0xFFFF6600),
                    ),
                    onPressed: _fetchTokenAndLaunch,
                    child: const Text(
                      'Apply Now',
                      style: TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                        color: Colors.white,
                      ),
                    ),
                  ),
                ],
              ),
            ),

            const SizedBox(height: 32),

            Theme(
              data: Theme.of(
                context,
              ).copyWith(dividerColor: Colors.transparent),
              child: ExpansionTile(
                title: const Text(
                  'Developer / SDK Settings',
                  style: TextStyle(fontWeight: FontWeight.w600),
                ),
                childrenPadding: const EdgeInsets.all(16),
                backgroundColor: Colors.white,
                collapsedBackgroundColor: Colors.white,
                children: [
                  TextField(
                    controller: _clientIdController,
                    decoration: const InputDecoration(
                      labelText: 'Client ID',
                      border: OutlineInputBorder(),
                      isDense: true,
                    ),
                  ),
                  const SizedBox(height: 12),
                  TextField(
                    controller: _clientSecretController,
                    decoration: const InputDecoration(
                      labelText: 'Client Secret',
                      border: OutlineInputBorder(),
                      isDense: true,
                    ),
                  ),
                  const SizedBox(height: 12),
                  SwitchListTile(
                    contentPadding: EdgeInsets.zero,
                    title: const Text(
                      "Use Current Time (Bypass OTP)",
                      style: TextStyle(fontSize: 14),
                    ),
                    value: _useCurrentTime,
                    activeColor: const Color(0xFFFF6600),
                    onChanged: (val) => setState(() => _useCurrentTime = val),
                  ),
                  const SizedBox(height: 12),
                  const Align(
                    alignment: Alignment.centerLeft,
                    child: Text(
                      'Allowed Securities (Leave unchecked for all):',
                      style: TextStyle(fontSize: 14),
                    ),
                  ),
                  CheckboxListTile(
                    contentPadding: EdgeInsets.zero,
                    title: const Text(
                      "Mutual Funds (LAMF)",
                      style: TextStyle(fontSize: 14),
                    ),
                    value: _allowLAMF,
                    activeColor: const Color(0xFFFF6600),
                    onChanged: (val) =>
                        setState(() => _allowLAMF = val ?? false),
                    controlAffinity: ListTileControlAffinity.leading,
                  ),
                  CheckboxListTile(
                    contentPadding: EdgeInsets.zero,
                    title: const Text(
                      "Shares (LAS)",
                      style: TextStyle(fontSize: 14),
                    ),
                    value: _allowLAS,
                    activeColor: const Color(0xFFFF6600),
                    onChanged: (val) =>
                        setState(() => _allowLAS = val ?? false),
                    controlAffinity: ListTileControlAffinity.leading,
                  ),
                  CheckboxListTile(
                    contentPadding: EdgeInsets.zero,
                    title: const Text(
                      "Insurance Policy (LAIP)",
                      style: TextStyle(fontSize: 14),
                    ),
                    value: _allowLAIP,
                    activeColor: const Color(0xFFFF6600),
                    onChanged: (val) =>
                        setState(() => _allowLAIP = val ?? false),
                    controlAffinity: ListTileControlAffinity.leading,
                  ),
                  const SizedBox(height: 12),
                  const Align(
                    alignment: Alignment.centerLeft,
                    child: Text(
                      'SDK Theme Color:',
                      style: TextStyle(fontSize: 14),
                    ),
                  ),
                  const SizedBox(height: 8),
                  Align(
                    alignment: Alignment.centerLeft,
                    child: Wrap(
                      spacing: 8,
                      children: _themeColors.map((color) {
                        return GestureDetector(
                          onTap: () =>
                              setState(() => _selectedThemeColor = color),
                          child: Container(
                            width: 36,
                            height: 36,
                            decoration: BoxDecoration(
                              color: color,
                              shape: BoxShape.circle,
                              border: Border.all(
                                color: _selectedThemeColor == color
                                    ? const Color(0xFF2C3178)
                                    : Colors.transparent,
                                width: 3,
                              ),
                            ),
                          ),
                        );
                      }).toList(),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}