kopia lustrzana https://gitlab.com/mysocialportal/relatica
Remove carousel from image viewer screen (renamed media viewer) to fix bad zoom behaviors
rodzic
6a5345953a
commit
581b4bf224
|
@ -4,7 +4,7 @@ import '../../globals.dart';
|
|||
import '../../models/image_entry.dart';
|
||||
import '../../models/visibility.dart';
|
||||
import '../../screens/existing_image_selector_screen.dart';
|
||||
import '../../screens/image_viewer_screen.dart';
|
||||
import '../../screens/media_viewer_screen.dart';
|
||||
import '../../serializers/friendica/image_entry_friendica_extensions.dart';
|
||||
import '../login_aware_cached_network_image.dart';
|
||||
import '../padding.dart';
|
||||
|
@ -66,7 +66,7 @@ class _GallerySelectorControlState extends State<GallerySelectorControl> {
|
|||
onDoubleTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ImageViewerScreen(
|
||||
builder: (context) => MediaViewerScreen(
|
||||
attachments: widget.entries
|
||||
.map((i) => i.toMediaAttachment())
|
||||
.toList(),
|
||||
|
|
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
|||
|
||||
import '../models/attachment_media_type_enum.dart';
|
||||
import '../models/media_attachment.dart';
|
||||
import '../screens/image_viewer_screen.dart';
|
||||
import '../screens/media_viewer_screen.dart';
|
||||
import 'audio_video/av_control.dart';
|
||||
import 'image_control.dart';
|
||||
|
||||
|
@ -49,7 +49,7 @@ class _MediaAttachmentViewerControlState
|
|||
altText: item.description,
|
||||
onTap: () async {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) {
|
||||
return ImageViewerScreen(
|
||||
return MediaViewerScreen(
|
||||
attachments: widget.attachments,
|
||||
initialIndex: widget.index,
|
||||
);
|
||||
|
|
|
@ -29,7 +29,7 @@ void main() async {
|
|||
WidgetsFlutterBinding.ensureInitialized();
|
||||
MediaKit.ensureInitialized();
|
||||
// await dotenv.load(fileName: '.env');
|
||||
const enablePreview = false;
|
||||
const enablePreview = true;
|
||||
Logger.root.level = Level.FINER;
|
||||
Logger.root.onRecord.listen((event) {
|
||||
final logName = event.loggerName.isEmpty ? 'ROOT' : event.loggerName;
|
||||
|
|
|
@ -8,7 +8,7 @@ import '../models/visibility.dart';
|
|||
import '../serializers/friendica/image_entry_friendica_extensions.dart';
|
||||
import '../services/gallery_service.dart';
|
||||
import '../utils/active_profile_selector.dart';
|
||||
import 'image_viewer_screen.dart';
|
||||
import 'media_viewer_screen.dart';
|
||||
|
||||
class ExistingImageSelectorScreen extends StatefulWidget {
|
||||
final Visibility? visibilityFilter;
|
||||
|
@ -171,7 +171,7 @@ class _ExistingImageSelectorScreenState
|
|||
onDoubleTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ImageViewerScreen(
|
||||
builder: (context) => MediaViewerScreen(
|
||||
attachments: [image.toMediaAttachment()],
|
||||
),
|
||||
),
|
||||
|
|
|
@ -11,7 +11,7 @@ import '../serializers/friendica/image_entry_friendica_extensions.dart';
|
|||
import '../services/gallery_service.dart';
|
||||
import '../services/network_status_service.dart';
|
||||
import '../utils/active_profile_selector.dart';
|
||||
import 'image_viewer_screen.dart';
|
||||
import 'media_viewer_screen.dart';
|
||||
|
||||
class GalleryScreen extends StatelessWidget {
|
||||
static const thumbnailDimension = 350.0;
|
||||
|
@ -117,7 +117,7 @@ class GalleryScreen extends StatelessWidget {
|
|||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (context) {
|
||||
return ImageViewerScreen(
|
||||
return MediaViewerScreen(
|
||||
attachments: attachments,
|
||||
initialIndex: index,
|
||||
);
|
||||
|
|
|
@ -1,153 +0,0 @@
|
|||
import 'dart:io';
|
||||
|
||||
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 '../controls/login_aware_cached_network_image.dart';
|
||||
import '../friendica_client/friendica_client.dart';
|
||||
import '../globals.dart';
|
||||
import '../models/media_attachment.dart';
|
||||
import '../services/auth_service.dart';
|
||||
import '../utils/clipboard_utils.dart';
|
||||
import '../utils/snackbar_builder.dart';
|
||||
|
||||
class ImageViewerScreen extends StatefulWidget {
|
||||
final List<MediaAttachment> attachments;
|
||||
final int initialIndex;
|
||||
|
||||
const ImageViewerScreen({
|
||||
super.key,
|
||||
required this.attachments,
|
||||
this.initialIndex = 0,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ImageViewerScreen> createState() => _ImageViewerScreenState();
|
||||
}
|
||||
|
||||
class _ImageViewerScreenState extends State<ImageViewerScreen> {
|
||||
MediaAttachment? currentAttachment;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
currentAttachment = widget.attachments[widget.initialIndex];
|
||||
}
|
||||
|
||||
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 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: LoginAwareCachedNetworkImage(
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
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/login_aware_cached_network_image.dart';
|
||||
import '../friendica_client/friendica_client.dart';
|
||||
import '../globals.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;
|
||||
}
|
||||
|
||||
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 height = MediaQuery.of(context).size.height;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => saveImage(context, currentAttachment),
|
||||
icon: const Icon(Icons.download))
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Stack(
|
||||
children: [
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: InteractiveViewer(
|
||||
maxScale: 10.0,
|
||||
scaleFactor: 400,
|
||||
child: LoginAwareCachedNetworkImage(
|
||||
imageUrl:
|
||||
widget.attachments[currentIndex].uri.toString()),
|
||||
),
|
||||
),
|
||||
if (currentAttachment.description.isNotEmpty)
|
||||
buildTextArea(currentAttachment),
|
||||
],
|
||||
),
|
||||
if (widget.attachments.length > 1) ...[
|
||||
Positioned(
|
||||
bottom: height * 0.5,
|
||||
child: Opacity(
|
||||
opacity: 0.8,
|
||||
child: currentIndex < 1
|
||||
? null
|
||||
: Container(
|
||||
color: Colors.black,
|
||||
child: IconButton(
|
||||
color: Colors.white,
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
currentIndex--;
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.chevron_left),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: height * 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: () {
|
||||
setState(() {
|
||||
currentIndex++;
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.chevron_right),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget buildTextArea(MediaAttachment attachment) {
|
||||
final height = MediaQuery.of(context).size.height * 0.1;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: SizedBox(
|
||||
height: height,
|
||||
child: attachment.description.isEmpty
|
||||
? null
|
||||
: 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Ładowanie…
Reference in New Issue