import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter_file_dialog/flutter_file_dialog.dart'; import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import 'package:relatica/utils/clipboard_utils.dart'; import '../friendica_client/friendica_client.dart'; import '../globals.dart'; import '../models/media_attachment.dart'; import '../services/auth_service.dart'; import '../utils/snackbar_builder.dart'; class ImageViewerScreen extends StatefulWidget { final List attachments; final int initialIndex; const ImageViewerScreen({ super.key, required this.attachments, this.initialIndex = 0, }); @override State createState() => _ImageViewerScreenState(); } class _ImageViewerScreenState extends State { MediaAttachment? currentAttachment; @override void initState() { super.initState(); currentAttachment = widget.attachments[widget.initialIndex]; } Future saveImage( BuildContext context, MediaAttachment attachment, ) async { buildSnackbar(context, 'Downloading full image to save locally'); final appsDir = await getApplicationDocumentsDirectory(); final filename = p.basename(attachment.fullFileUri.path); final bytesResult = await RemoteFileClient(getIt().currentProfile) .getFileBytes(attachment.uri); if (bytesResult.isFailure && mounted) { buildSnackbar(context, 'Error getting full size version of file: ${bytesResult.error}'); return; } if (Platform.isAndroid || Platform.isIOS) { final params = SaveFileDialogParams( data: bytesResult.value, fileName: filename, ); await FlutterFileDialog.saveFile(params: params); } else { final location = await FilePicker.platform.saveFile( dialogTitle: 'Save Image', fileName: filename, initialDirectory: appsDir.path, ); if (location != null) { await File(location).writeAsBytes(bytesResult.value); } } } @override Widget build(BuildContext context) { final width = MediaQuery.of(context).size.width; final height = MediaQuery.of(context).size.height; final carouselHeight = widget.attachments.length == 1 ? height * 0.9 : height * 0.8; return Scaffold( appBar: AppBar( actions: [ IconButton( onPressed: currentAttachment == null ? null : () => saveImage(context, currentAttachment!), icon: const Icon(Icons.download)) ], ), body: SafeArea( child: Column( children: [ CarouselSlider.builder( disableGesture: true, itemCount: widget.attachments.length, itemBuilder: (context, index, realIndex) { return SizedBox( width: width, height: carouselHeight, child: InteractiveViewer( maxScale: 10.0, scaleFactor: 400, child: CachedNetworkImage( imageUrl: widget.attachments[index].uri.toString()), ), ); }, options: CarouselOptions( height: carouselHeight, initialPage: widget.initialIndex, enableInfiniteScroll: false, enlargeCenterPage: true, viewportFraction: 0.95, onPageChanged: (index, reason) { setState(() { currentAttachment = widget.attachments[index]; }); }), ), if (currentAttachment != null) buildTextArea(currentAttachment!), ], ), ), ); } Widget buildTextArea(MediaAttachment attachment) { return Expanded( child: Padding( padding: const EdgeInsets.all(8.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: SingleChildScrollView( child: Text(attachment.description), )), IconButton( onPressed: () async { await copyToClipboard( context: context, text: attachment.description, message: 'Image description copied to clipboard'); }, icon: Icon(Icons.copy), ), ], ), ), ); } }