welloo_sdk 0.0.76 copy "welloo_sdk: ^0.0.76" to clipboard
welloo_sdk: ^0.0.76 copied to clipboard

Package de transaction Welloo

example/lib/main.dart

import 'dart:async';

import 'package:app_links/app_links.dart';
import 'package:example/auth_service.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:welloo_sdk/welloo_sdk.dart';

// 1. DÉCLARATION GLOBALE DE LA CLÉ DE NAVIGATION
// Cela permet d'accéder au navigateur depuis n'importe où (même hors du contexte)
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // 2. CHARGEMENT DES VARIABLES D'ENVIRONNEMENT
  try {
    await dotenv.load(fileName: '.env');
  } catch (e) {
    debugPrint('⚠️ Fichier .env introuvable. Assurez-vous d\'en créer un.');
  }

  runApp(const MainApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welloo SDK - Nouveau Système',
      // 3. INJECTION DE LA CLÉ DANS MATERIAL APP
      navigatorKey: navigatorKey,
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorSchemeSeed: Colors.blue,
        useMaterial3: true,
      ),
      home: const DemoWello(),
    );
  }
}

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

  @override
  State<DemoWello> createState() => _DemoWelloState();
}

class _DemoWelloState extends State<DemoWello> {
  bool _isLoggedIn = false;
  bool _isLoading = false; // Pour afficher un spinner
  final TextEditingController _phoneController = TextEditingController();
  final TextEditingController _pineController = TextEditingController();
  final AuthService _authService = AuthService();

  // Getters pour sécuriser l'accès aux variables d'env
  String _accessToken = "";
  String _refreshToken = '';
  String get _successUrl => dotenv.env['LINK_SUCCESS'] ?? '';
  String get _errorUrl => dotenv.env['LINK_ERROR'] ?? '';

  StreamSubscription<Uri>? _linkSubscription;

  @override
  void initState() {
    super.initState();
    initDeepLinks();
  }

  @override
  void dispose() {
    _linkSubscription?.cancel();
    super.dispose();
  }

  void _handleLogin() async {
    // Ici, vous pouvez ajouter votre logique réelle d'appel API
    print("Numero : ${_phoneController.text}");
    print("Pin : ${_pineController.text}");
    // if (_phoneController.text.isNotEmpty && _pineController.text.isNotEmpty) {
    //   _showError("Veuillez remplir tous les champs");
    //   return;
    // }
    setState(() => _isLoading = true);

    final result =
        await _authService.login(_phoneController.text, _pineController.text);

    setState(() => _isLoading = false);

    if (result != null) {
      setState(() {
        _accessToken = result['access_token'];
        _refreshToken = result['refresh_token'];
        _isLoggedIn = true;
      });
      print("reponse : ${result['access_token']}");
      // Ici vous pouvez stocker le token retourné par l'API dans votre dotenv ou storage
      // setState(() => _isLoggedIn = true);
    } else {
      _showError("Identifiants incorrects ou erreur serveur");
    }
  }

  void _showError(String message) {
    ScaffoldMessenger.of(context)
        .showSnackBar(SnackBar(content: Text(message)));
  }

  // --- LOGIQUE DEEP LINK ---

  Future<void> initDeepLinks() async {
    _linkSubscription = AppLinks().uriLinkStream.listen((uri) {
      debugPrint('🔗 DeepLink reçu : $uri');

      // CAS 1 : Retour de Paiement Finapay
      if (uri.host == 'api-transaction-dev.finapay.net') {
        // Petit délai pour assurer que l'UI est prête
        // Future.delayed(const Duration(milliseconds: 500), () {
        //   debugPrint("🚀 Navigation vers le résultat de paiement...");

        //   // Utilisation de la clé globale définie tout en haut
        //   navigatorKey.currentState?.pushAndRemoveUntil(
        //     MaterialPageRoute(
        //       builder: (context) => const Scaffold(
        //         body: Center(
        //           child: Column(
        //             mainAxisAlignment: MainAxisAlignment.center,
        //             children: [
        //               Icon(Icons.check_circle, color: Colors.green, size: 80),
        //               SizedBox(height: 20),
        //               Text('Paiement Terminé !',
        //                   style: TextStyle(fontSize: 20)),
        //             ],
        //           ),
        //         ),
        //       ),
        //     ),
        //     (route) => false, // Supprime tout l'historique
        //     // (route) => route.isFirst // Alternative : Garder l'accueil derrière
        //   );
        // });
      }
      // CAS 2 : Autres liens
      else {
        if (uri.fragment.isNotEmpty) {
          navigatorKey.currentState?.pushNamed(uri.fragment);
        }
      }
    });
  }

  // --- HANDLERS DU SDK ---

  Future<void> _handleDeposit() async {
    Navigator.of(context).push(MaterialPageRoute(
        builder: (_) => WellooDeposit(
              accessToken: _accessToken,
              refreshToken: _refreshToken,
              // errorUrl: Uri.parse(_errorUrl),
              // successUrl: Uri.parse(_successUrl),

              // ✅ Callback Succès
              onSuccess: (result) {
                debugPrint("🎉 SUCCÈS PAIEMENT");
                debugPrint("-------------------");
                debugPrint("Ref  : ${result.transactionId}");
                debugPrint("Info : ${result.description}");
              },

              // ❌ Callback Erreur (Refus, Annulation, Technique)
              onError: (error) {
                debugPrint("⚠️ ÉCHEC PAIEMENT");
                debugPrint("------------------");
                debugPrint("Cause : ${error.description}");
              },
            )));
  }

  Future<void> _handleTransfer() async {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) => WellooTransfer(
          accessToken: _accessToken,
          refreshToken: _refreshToken,
          // ✅ Callback Succès
          onSuccess: (result) {
            debugPrint("Info : ${result.description}");
          },
          // ❌ Callback Erreur
          onError: (error) {
            debugPrint("Error xcfxd : ${error.description}");
          },
        ),
      ),
    );
  }

  Future<void> _handleScanner() async {
    Navigator.of(context).push(
      MaterialPageRoute(
        builder: (_) => WellooScanQr(
          accessToken: _accessToken,
          refreshToken: _refreshToken,
          // ✅ Callback Succès
          onSuccess: (result) {
            debugPrint("✅ Succès : ${result.description}");
          },
          // ❌ Callback Erreur
          onError: (error) {
            debugPrint("❌ Erreur : ${error.description}");
          },
        ),
      ),
    );
  }

  // --- UI ---

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Welloo SDK Demo'),
        centerTitle: true,
      ),
      backgroundColor: Colors.grey[100],
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(20.0),
          child: _isLoggedIn ? _buildMainMenu() : _buildLoginForm(),
        ),
      ),
    );
  }

  // Formulaire de connexion
  Widget _buildLoginForm() {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        const Icon(Icons.account_circle, size: 100, color: Colors.blue),
        const SizedBox(height: 20),
        const Text("Bienvenue sur Welloo",
            style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
        const SizedBox(height: 30),
        TextField(
          controller: _phoneController,
          keyboardType: TextInputType.phone,
          decoration: InputDecoration(
            labelText: "Numéro de téléphone",
            border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
            prefixIcon: const Icon(Icons.phone),
          ),
        ),
        const SizedBox(height: 20),
        TextField(
          controller: _pineController,
          keyboardType: TextInputType.phone,
          decoration: InputDecoration(
            labelText: "Pin",
            border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
            prefixIcon: const Icon(Icons.lock),
          ),
        ),
        const SizedBox(height: 20),
        SizedBox(
          width: double.infinity,
          height: 55,
          child: ElevatedButton(
            onPressed: _handleLogin,
            style: ElevatedButton.styleFrom(
              backgroundColor: Colors.blue,
              foregroundColor: Colors.white,
              shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12)),
            ),
            child: const Text("Se connecter", style: TextStyle(fontSize: 18)),
          ),
        ),
      ],
    );
  }

  // Votre menu actuel déplacé dans une fonction
  Widget _buildMainMenu() {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        _buildMenuButton(
          icon: Icons.download,
          label: "Initier un Dépôt",
          onPressed: _handleDeposit,
          color: Colors.green,
        ),
        const SizedBox(height: 20),
        _buildMenuButton(
          icon: Icons.send,
          label: "Initier un Transfert",
          onPressed: _handleTransfer,
          color: Colors.blue,
        ),
        const SizedBox(height: 20),
        _buildMenuButton(
          icon: Icons.qr_code_scanner,
          label: "Scanner QR Code",
          onPressed: _handleScanner,
          color: Colors.purple,
        ),
      ],
    );
  }

  // Widget helper pour éviter la répétition de code
  Widget _buildMenuButton({
    required IconData icon,
    required String label,
    required VoidCallback onPressed,
    required Color color,
  }) {
    return SizedBox(
      width: double.infinity,
      height: 60,
      child: ElevatedButton.icon(
        onPressed: onPressed,
        icon: Icon(icon),
        label: Text(label, style: const TextStyle(fontSize: 16)),
        style: ElevatedButton.styleFrom(
          backgroundColor: color,
          foregroundColor: Colors.white,
          elevation: 2,
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
        ),
      ),
    );
  }
}