relatica/lib/screens/media_viewer_screen.dart

232 wiersze
7.1 KiB
Dart

import 'dart:io';
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 '../controls/audio_video/av_control.dart';
import '../controls/login_aware_cached_network_image.dart';
import '../friendica_client/friendica_client.dart';
import '../globals.dart';
import '../models/attachment_media_type_enum.dart';
import '../models/media_attachment.dart';
import '../services/auth_service.dart';
import '../utils/clipboard_utils.dart';
import '../utils/snackbar_builder.dart';
class MediaViewerScreen extends StatefulWidget {
final List<MediaAttachment> attachments;
final int initialIndex;
const MediaViewerScreen({
super.key,
required this.attachments,
this.initialIndex = 0,
});
@override
State<MediaViewerScreen> createState() => _MediaViewerScreenState();
}
class _MediaViewerScreenState extends State<MediaViewerScreen> {
var currentIndex = 0;
@override
void initState() {
super.initState();
currentIndex = widget.initialIndex;
}
void nextAttachment() {
if (currentIndex >= widget.attachments.length - 1) {
return;
}
setState(() {
currentIndex++;
});
}
void previousAttachment() {
if (currentIndex < 1) {
return;
}
setState(() {
currentIndex--;
});
}
Future<void> 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<AccountsService>().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 currentAttachment = widget.attachments[currentIndex];
final width = MediaQuery.of(context).size.width;
final height = MediaQuery.of(context).size.height * 0.9;
final descriptionHeightPct = widget.attachments.isEmpty ? 0.0 : 0.1;
final mediaHeight = height * (1 - descriptionHeightPct);
final descriptionHeight = height * descriptionHeightPct;
late final bool canSave;
late final Widget mediaViewer;
switch (currentAttachment.explicitType) {
case AttachmentMediaType.image:
canSave = true;
mediaViewer = SizedBox(
width: width,
height: mediaHeight,
child: InteractiveViewer(
maxScale: 10.0,
scaleFactor: 400,
child: LoginAwareCachedNetworkImage(
imageUrl: currentAttachment.uri.toString()),
));
break;
case AttachmentMediaType.unknown:
case AttachmentMediaType.video:
canSave = false;
if (Platform.isLinux) {
mediaViewer = Text(
'No media viewer for ${currentAttachment.explicitType.name} type for link ${currentAttachment.fullFileUri}');
} else {
mediaViewer = SizedBox(
width: width,
height: mediaHeight,
child: AVControl(
videoUrl: currentAttachment.fullFileUri.toString(),
description: currentAttachment.description,
),
);
}
break;
}
return Scaffold(
appBar: AppBar(
actions: [
if (canSave)
IconButton(
onPressed: () => saveImage(context, currentAttachment),
icon: const Icon(Icons.download))
],
),
body: SafeArea(
child: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
mediaViewer,
if (currentAttachment.description.isNotEmpty)
buildTextArea(currentAttachment, descriptionHeight),
],
),
if (widget.attachments.length > 1) ...[
Positioned(
bottom: mediaHeight * 0.5,
child: Opacity(
opacity: 0.8,
child: currentIndex < 1
? null
: Container(
color: Colors.black,
child: IconButton(
color: Colors.white,
onPressed: previousAttachment,
icon: const Icon(Icons.chevron_left),
),
),
),
),
Positioned(
bottom: mediaHeight * 0.5,
right: 0.0,
child: Opacity(
opacity: 0.8,
child: currentIndex >= widget.attachments.length - 1
? null
: Container(
color: Colors.black,
child: IconButton(
color: Colors.white,
onPressed: nextAttachment,
icon: const Icon(Icons.chevron_right),
),
),
),
),
]
],
),
),
);
}
Widget buildTextArea(MediaAttachment attachment, double height) {
return Padding(
padding: const EdgeInsets.only(left: 8.0, right: 8.0),
child: SizedBox(
height: height,
child: attachment.description.isEmpty
? null
: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Scrollbar(
controller: ScrollController(),
thumbVisibility: true,
child: SingleChildScrollView(
child: Text(attachment.description),
),
)),
IconButton(
onPressed: () async {
await copyToClipboard(
context: context,
text: attachment.description,
message: 'Image description copied to clipboard');
},
icon: const Icon(Icons.copy),
),
],
),
),
);
}
}