✨ Video player!
This commit is contained in:
@ -1,9 +1,12 @@
|
||||
import 'package:chewie/chewie.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:solian/models/attachment.dart';
|
||||
import 'package:solian/services.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
class AttachmentItem extends StatelessWidget {
|
||||
class AttachmentItem extends StatefulWidget {
|
||||
final String parentId;
|
||||
final Attachment item;
|
||||
final bool showBadge;
|
||||
@ -24,45 +27,127 @@ class AttachmentItem extends StatelessWidget {
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Hero(
|
||||
tag: Key('a${item.uuid}p$parentId'),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Image.network(
|
||||
'${ServiceFinder.services['paperclip']}/api/attachments/${item.id}',
|
||||
fit: fit,
|
||||
),
|
||||
if (showBadge && badge != null)
|
||||
Positioned(
|
||||
right: 12,
|
||||
bottom: 8,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Chip(label: Text(badge!)),
|
||||
),
|
||||
),
|
||||
if (showHideButton && item.isMature)
|
||||
Positioned(
|
||||
top: 8,
|
||||
left: 12,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: ActionChip(
|
||||
visualDensity:
|
||||
const VisualDensity(vertical: -4, horizontal: -4),
|
||||
avatar: Icon(Icons.visibility_off,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
label: Text('hide'.tr),
|
||||
onPressed: () {
|
||||
if (onHide != null) onHide!();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
State<AttachmentItem> createState() => _AttachmentItemState();
|
||||
}
|
||||
|
||||
class _AttachmentItemState extends State<AttachmentItem> {
|
||||
VideoPlayerController? _videoPlayerController;
|
||||
ChewieController? _chewieController;
|
||||
|
||||
void ensureInitVideo() {
|
||||
if (_videoPlayerController != null) return;
|
||||
|
||||
_videoPlayerController = VideoPlayerController.networkUrl(Uri.parse(
|
||||
'${ServiceFinder.services['paperclip']}/api/attachments/${widget.item.id}',
|
||||
));
|
||||
_videoPlayerController!.initialize();
|
||||
_chewieController = ChewieController(
|
||||
aspectRatio: widget.item.metadata?['ratio'] ?? 16 / 9,
|
||||
videoPlayerController: _videoPlayerController!,
|
||||
customControls: const MaterialControls(showPlayButton: true),
|
||||
materialProgressColors: ChewieProgressColors(
|
||||
playedColor: Theme.of(context).colorScheme.primary,
|
||||
handleColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
switch (widget.item.mimetype.split('/').first) {
|
||||
case 'image':
|
||||
return Hero(
|
||||
tag: Key('a${widget.item.uuid}p${widget.parentId}'),
|
||||
child: Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
Image.network(
|
||||
'${ServiceFinder.services['paperclip']}/api/attachments/${widget.item.id}',
|
||||
fit: widget.fit,
|
||||
),
|
||||
if (widget.showBadge && widget.badge != null)
|
||||
Positioned(
|
||||
right: 12,
|
||||
bottom: 8,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: Chip(label: Text(widget.badge!)),
|
||||
),
|
||||
),
|
||||
if (widget.showHideButton && widget.item.isMature)
|
||||
Positioned(
|
||||
top: 8,
|
||||
left: 12,
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: ActionChip(
|
||||
visualDensity:
|
||||
const VisualDensity(vertical: -4, horizontal: -4),
|
||||
avatar: Icon(Icons.visibility_off,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurfaceVariant),
|
||||
label: Text('hide'.tr),
|
||||
onPressed: () {
|
||||
if (widget.onHide != null) widget.onHide!();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
case 'video':
|
||||
ensureInitVideo();
|
||||
return Chewie(controller: _chewieController!);
|
||||
default:
|
||||
return Center(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: 280,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Icon(Icons.file_present, size: 32),
|
||||
const SizedBox(height: 6),
|
||||
Text(
|
||||
widget.item.mimetype,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
widget.item.alt,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
TextButton.icon(
|
||||
icon: const Icon(Icons.launch),
|
||||
label: Text('openInBrowser'.tr),
|
||||
style: const ButtonStyle(
|
||||
visualDensity: VisualDensity(vertical: -2, horizontal: -4),
|
||||
),
|
||||
onPressed: () {
|
||||
launchUrlString(
|
||||
'${ServiceFinder.services['paperclip']}/api/attachments/${widget.item.id}',
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_videoPlayerController?.dispose();
|
||||
_chewieController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,11 @@ class _AttachmentListState extends State<AttachmentList> {
|
||||
int portrait = 0, square = 0, landscape = 0;
|
||||
for (var entry in _attachmentsMeta) {
|
||||
if (entry!.metadata?['ratio'] != null) {
|
||||
consistentValue ??= entry.metadata?['ratio'];
|
||||
if (entry.metadata?['ratio'] is int) {
|
||||
consistentValue ??= entry.metadata?['ratio'].toDouble();
|
||||
} else {
|
||||
consistentValue ??= entry.metadata?['ratio'];
|
||||
}
|
||||
if (isConsistent && entry.metadata?['ratio'] != consistentValue) {
|
||||
isConsistent = false;
|
||||
}
|
||||
@ -180,7 +184,7 @@ class _AttachmentListState extends State<AttachmentList> {
|
||||
onTap: () {
|
||||
if (!_showMature && _attachmentsMeta.any((e) => e!.isMature)) {
|
||||
setState(() => _showMature = true);
|
||||
} else {
|
||||
} else if (['image'].contains(element.mimetype.split('/').first)) {
|
||||
Navigator.of(context, rootNavigator: true).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => AttachmentListFullScreen(
|
||||
|
Reference in New Issue
Block a user