// lib/presentation/screens/text_generation/text_generation_screen.dart import 'dart:convert'; import 'package:flutter/material.dart'; import '../../../repositories/ai_repository.dart'; import '../../../routes/app_routes.dart'; import '../../widgets/creation_flow_layout.dart'; import '../post_refinement/post_refinement_screen.dart'; class TextGenerationScreenArguments { TextGenerationScreenArguments({ required this.imageBase64, required this.aiRepository, }); final String imageBase64; final AiRepository aiRepository; } final class TextGenerationScreen extends StatefulWidget { const TextGenerationScreen({required this.arguments, super.key}); final TextGenerationScreenArguments arguments; @override State createState() => _TextGenerationScreenState(); } class _TextGenerationScreenState extends State { 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(); } // --- MÉTHODE CORRIGÉE ET SIMPLIFIÉE --- 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 vers Ollama...'); // 1. On récupère directement la liste de chaînes de caractères. 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 d'Ollama."); // 2. La logique de découpage ('split') est supprimée car inutile. if (ideas.isEmpty) { _addLog('⚠️ Avertissement : Le service a retourné une liste vide.'); } else { _addLog('📦 ${ideas.length} idée(s) reçue(s).'); } // 3. On met à jour l'état directement avec la liste reçue. setState(() { _generatedIdeas = ideas; }); _addLog('✨ Interface mise à jour avec la liste des idées.'); } 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.'); } } void _navigateToRefinementScreen(String selectedIdea) { Navigator.pushNamed( context, AppRoutes.postRefinement, arguments: PostRefinementScreenArguments( initialText: selectedIdea, imageBase64: widget.arguments.imageBase64, aiRepository: widget.arguments.aiRepository, ), ); } @override Widget build(BuildContext context) { // Le reste du fichier build est identique et correct. return CreationFlowLayout( currentStep: 4, title: '4. Génération de Texte', child: Stack( children: [ SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Center( child: ConstrainedBox( constraints: const BoxConstraints(maxHeight: 200), child: ClipRRect( borderRadius: BorderRadius.circular(12), 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: 5, // On peut garder plus de lignes overflow: TextOverflow.ellipsis), leading: CircleAvatar(child: Text('${index + 1}')), trailing: const Icon(Icons.edit_note), onTap: () => _navigateToRefinementScreen(idea))); }, ), ], const SizedBox(height: 150), ], ), ), 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), 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( color: log.contains('❌') ? Theme.of(context).colorScheme.error : (log.contains('✅') || log.contains('📦') || log.contains('✨')) ? Colors.green[600] : Theme.of(context) .colorScheme .onSurface .withOpacity(0.8), fontSize: 12 ), ); }, ), ), ), ), ], ), ); } }