// lib/presentation/screens/text_generation/text_generation_screen.dart import 'dart:convert'; import 'package:flutter/material.dart';// --- CORRECTION 1 : IMPORTER LE REPOSITORY --- import '../../../repositories/ai_repository.dart'; import '../../../routes/app_routes.dart'; import '../../widgets/creation_flow_layout.dart'; import '../post_refinement/post_refinement_screen.dart'; // Import pour la classe d'arguments // --- CORRECTION 2 : DÉFINIR UNE CLASSE D'ARGUMENTS PROPRE --- class TextGenerationScreenArguments { final String imageBase64; final AiRepository aiRepository; TextGenerationScreenArguments({ required this.imageBase64, required this.aiRepository, }); } final class TextGenerationScreen extends StatefulWidget { // Le constructeur attend maintenant la classe d'arguments. final TextGenerationScreenArguments arguments; const TextGenerationScreen({super.key, required this.arguments}); @override State createState() => _TextGenerationScreenState(); } class _TextGenerationScreenState extends State { // Les variables d'état et contrôleurs restent inchangés bool _loading = false; List _generatedIdeas = []; final List _logs = []; final _professionController = TextEditingController(text: 'Entrepreneur digital'); final _toneController = TextEditingController(text: 'Professionnel et engageant'); @override void initState() { super.initState(); _addLog("🖼️ Image reçue avec succès."); } void _addLog(String log) { if (mounted) { setState(() => _logs.insert(0, log)); } } @override void dispose() { _professionController.dispose(); _toneController.dispose(); super.dispose(); } // --- CORRECTION 3 : UTILISER LE REPOSITORY POUR LA GÉNÉRATION --- Future _handleGenerateIdeas() async { if (_loading) { _addLog("⏳ Annulation : Génération déjà en cours."); return; } if (!mounted) return; setState(() { _loading = true; _generatedIdeas = []; _logs.clear(); }); _addLog("▶️ Lancement de la génération d'idées..."); try { final profession = _professionController.text; final tone = _toneController.text; _addLog("⚙️ Paramètres : Métier='${profession}', Ton='${tone}'."); _addLog("🚀 Appel du AiRepository..."); // On appelle la méthode du Repository, qui délègue au bon service. final ideas = await widget.arguments.aiRepository.generatePostIdeas( base64Image: widget.arguments.imageBase64, profession: profession, tone: tone, ); if (!mounted) return; _addLog("✅ Succès ! Réponse reçue."); if (ideas.isEmpty) { _addLog("⚠️ Avertissement : Le service a retourné une liste vide."); } else { _addLog("📦 ${ideas.length} idée(s) reçue(s)."); } setState(() { _generatedIdeas = ideas; }); _addLog("✨ Interface mise à jour."); } catch (e) { if (!mounted) return; _addLog("❌ ERREUR : ${e.toString()}"); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text('Erreur: ${e.toString()}'), backgroundColor: Colors.red)); } finally { if (mounted) { setState(() => _loading = false); } _addLog("⏹️ Fin du processus."); } } // --- CORRECTION 4 : NAVIGATION PROPRE VERS L'ÉCRAN D'AFFINAGE --- void _navigateToRefinementScreen(String selectedIdea) { Navigator.pushNamed( context, AppRoutes.postRefinement, arguments: PostRefinementScreenArguments( initialText: selectedIdea, imageBase64: widget.arguments.imageBase64, aiRepository: widget.arguments.aiRepository, // On passe le Repository ), ); } @override Widget build(BuildContext context) { // La structure du build reste la même, mais je corrige l'index de l'étape. return CreationFlowLayout( currentStep: 4, // C'est la 5ème étape (index 4) title: '4. Génération de Texte', child: Stack( children: [ SingleChildScrollView( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: ConstrainedBox( constraints: const BoxConstraints(maxHeight: 200), child: ClipRRect( borderRadius: BorderRadius.circular(12.0), child: Image.memory( base64Decode(widget.arguments.imageBase64), width: double.infinity, fit: BoxFit.cover, ), ), ), ), const SizedBox(height: 24), TextField( controller: _professionController, decoration: const InputDecoration( labelText: 'Votre métier', border: OutlineInputBorder(), prefixIcon: Icon(Icons.work_outline))), const SizedBox(height: 16), TextField( controller: _toneController, decoration: const InputDecoration( labelText: 'Ton souhaité', border: OutlineInputBorder(), prefixIcon: Icon(Icons.campaign_outlined))), const SizedBox(height: 24), SizedBox( width: double.infinity, child: FilledButton.icon( onPressed: _loading ? null : _handleGenerateIdeas, icon: _loading ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2)) : const Icon(Icons.auto_awesome), label: const Text('Générer 3 idées de post'), style: FilledButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16)), ), ), const SizedBox(height: 24), if (_generatedIdeas.isNotEmpty) ...[ const Divider(height: 32), Text("Choisissez une idée à affiner", style: Theme.of(context) .textTheme .titleMedium ?.copyWith(fontWeight: FontWeight.bold)), const SizedBox(height: 16), ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: _generatedIdeas.length, itemBuilder: (context, index) { final idea = _generatedIdeas[index]; return Card( margin: const EdgeInsets.only(bottom: 12), child: ListTile( title: Text(idea, maxLines: 3, overflow: TextOverflow.ellipsis), leading: CircleAvatar(child: Text('${index + 1}')), trailing: const Icon(Icons.edit_note), onTap: () => _navigateToRefinementScreen(idea))); }, ), ], const SizedBox(height: 150), // Espace pour la console de logs ], ), ), // La console de logs reste inchangée Positioned( bottom: 0, left: 0, right: 0, child: IgnorePointer( child: Container( height: 140, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( gradient: LinearGradient( colors: [ Theme.of(context).scaffoldBackgroundColor.withOpacity(0.0), Theme.of(context).scaffoldBackgroundColor.withOpacity(0.6), Theme.of(context).scaffoldBackgroundColor, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: ListView.builder( reverse: true, itemCount: _logs.length, itemBuilder: (context, index) { final log = _logs[index]; return Text(log, style: TextStyle( // Logique de couleur conditionnelle pour la lisibilité color: log.contains('❌') ? Theme.of(context).colorScheme.error // Rouge pour les erreurs : (log.contains('✅') || log.contains('📦') || log.contains('✨')) ? Colors.green[600] // Vert pour les succès : Theme.of(context) .colorScheme .onSurface .withOpacity(0.8), // Couleur par défaut fontSize: 12 ), ); }, ), ), ), ), ], ), ); } }