import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flux/core/utils/functions.dart'; import 'package:flux/features/attachments/models/attachment_model.dart'; import 'package:pdfx/pdfx.dart'; import 'package:internet_file/internet_file.dart'; class AttachmentViewerScreen extends StatefulWidget { final AttachmentModel attachment; final Function(String newName)? onRename; final VoidCallback? onDelete; const AttachmentViewerScreen({ super.key, required this.attachment, this.onRename, this.onDelete, }); @override State createState() => _AttachmentViewerScreenState(); } class _AttachmentViewerScreenState extends State { PdfControllerPinch? _pdfController; bool _isLoading = true; String? _errorMessage; Uint8List? _fileBytes; late String _fileName; bool get isPdf => widget.attachment.extension.toLowerCase() == 'pdf'; @override void initState() { super.initState(); _fileName = widget.attachment.name; _loadFile(); } Future _loadFile() async { try { // 1. Capiamo da dove prendere i dati if (widget.attachment.localBytes != null) { _fileBytes = widget.attachment.localBytes; } else if (widget.attachment.storagePath != null && widget.attachment.storagePath!.isNotEmpty) { final signedUrl = await getSignedUrl(widget.attachment.storagePath!); _fileBytes = await InternetFile.get(signedUrl); } else { throw Exception("Nessun documento trovato o byte mancanti."); } // 2. Se è PDF, inizializziamo il controller if (isPdf && _fileBytes != null) { _pdfController = PdfControllerPinch( document: PdfDocument.openData(_fileBytes!), ); } if (mounted) setState(() => _isLoading = false); } catch (e) { if (mounted) { setState(() { _isLoading = false; _errorMessage = e.toString(); }); } } } @override void dispose() { _pdfController?.dispose(); super.dispose(); } void _showRenameDialog() { final ctrl = TextEditingController(text: _fileName); ctrl.selection = TextSelection( baseOffset: 0, extentOffset: ctrl.text.length, ); final focusNode = FocusNode(); showDialog( context: context, builder: (context) { WidgetsBinding.instance.addPostFrameCallback( (_) => focusNode.requestFocus(), ); return AlertDialog( title: const Text('Rinomina File'), content: TextField( controller: ctrl, focusNode: focusNode, decoration: InputDecoration( labelText: 'Nuovo nome', suffixText: '.${widget.attachment.extension}', ), onSubmitted: (val) { Navigator.pop(context); if (val.trim().isNotEmpty && widget.onRename != null) { setState(() { _fileName = val.trim(); }); widget.onRename!(val.trim()); } }, ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: const Text('Annulla'), ), ElevatedButton( onPressed: () { Navigator.pop(context); if (ctrl.text.trim().isNotEmpty && widget.onRename != null) { setState(() { _fileName = ctrl.text.trim(); }); widget.onRename!(ctrl.text.trim()); } }, child: const Text('Salva'), ), ], ); }, ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.black87, // Sfondo scuro per i viewer è il top appBar: AppBar( backgroundColor: Colors.black, foregroundColor: Colors.white, title: Text(_fileName, style: const TextStyle(fontSize: 16)), actions: [ if (widget.onRename != null) IconButton( icon: const Icon(Icons.edit), tooltip: 'Rinomina', onPressed: _showRenameDialog, ), if (widget.onDelete != null) IconButton( icon: const Icon(Icons.delete, color: Colors.redAccent), tooltip: 'Elimina', onPressed: () { // Chiediamo conferma showDialog( context: context, builder: (c) => AlertDialog( title: const Text('Eliminare file?'), content: const Text( 'Sei sicuro di voler eliminare questo allegato?', ), actions: [ TextButton( onPressed: () => Navigator.pop(c), child: const Text('Annulla'), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.red, ), onPressed: () { Navigator.pop(c); // Chiude dialog widget.onDelete!(); // Lancia eliminazione Navigator.pop(context); // Chiude il viewer }, child: const Text('Elimina'), ), ], ), ); }, ), ], ), body: _buildBody(), ); } Widget _buildBody() { if (_isLoading) { return const Center( child: CircularProgressIndicator(color: Colors.white), ); } if (_errorMessage != null) { return Center( child: Text( 'Errore: $_errorMessage', style: const TextStyle(color: Colors.redAccent), ), ); } if (_fileBytes == null) { return const Center( child: Text( 'File non disponibile', style: TextStyle(color: Colors.white), ), ); } if (isPdf && _pdfController != null) { return PdfViewPinch(controller: _pdfController!); } else { return InteractiveViewer( maxScale: 5.0, child: Center(child: Image.memory(_fileBytes!)), ); } } }