welloo_sdk 0.0.76
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)),
),
),
);
}
}