|
- // 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<TextGenerationScreen> createState() => _TextGenerationScreenState();
- }
-
- class _TextGenerationScreenState extends State<TextGenerationScreen> {
- // Les variables d'état et contrôleurs restent inchangés
- bool _loading = false;
- List<String> _generatedIdeas = [];
- final List<String> _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<void> _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
- ),
- );
-
- },
- ),
- ),
- ),
- ),
- ],
- ),
- );
- }
- }
|