mirror of
https://gitlab.com/mysocialportal/relatica
synced 2024-10-18 13:33:32 +00:00
Add video player for mobile platforms
This commit is contained in:
parent
cd41a0c581
commit
3d7f1b53c0
3 changed files with 177 additions and 11 deletions
|
@ -56,6 +56,8 @@ PODS:
|
|||
- SwiftyGif (5.4.3)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- video_player_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
|
@ -67,6 +69,7 @@ DEPENDENCIES:
|
|||
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
|
||||
- sqflite (from `.symlinks/plugins/sqflite/ios`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
|
@ -95,6 +98,8 @@ EXTERNAL SOURCES:
|
|||
:path: ".symlinks/plugins/sqflite/ios"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
video_player_avfoundation:
|
||||
:path: ".symlinks/plugins/video_player_avfoundation/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac
|
||||
|
@ -111,6 +116,7 @@ SPEC CHECKSUMS:
|
|||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
||||
url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de
|
||||
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
|
||||
|
||||
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../globals.dart';
|
||||
import '../models/attachment_media_type_enum.dart';
|
||||
import '../models/media_attachment.dart';
|
||||
import '../screens/image_viewer_screen.dart';
|
||||
import '../utils/snackbar_builder.dart';
|
||||
import 'image_control.dart';
|
||||
import 'video_control.dart';
|
||||
|
||||
class MediaAttachmentViewerControl extends StatefulWidget {
|
||||
final MediaAttachment attachment;
|
||||
|
@ -22,29 +24,65 @@ class _MediaAttachmentViewerControlState
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final item = widget.attachment;
|
||||
const width = 250.0;
|
||||
const height = 250.0;
|
||||
if (item.explicitType == AttachmentMediaType.video) {
|
||||
return ElevatedButton(
|
||||
onPressed: () async {
|
||||
if (useVideoPlayer) {
|
||||
return VideoControl(
|
||||
videoUrl: widget.attachment.uri.toString(),
|
||||
width: width,
|
||||
height: height,
|
||||
);
|
||||
}
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
final confirm = await showYesNoDialog(
|
||||
context, 'Open Video Link in external app? ${item.uri}');
|
||||
if (confirm != true) {
|
||||
return;
|
||||
}
|
||||
if (await canLaunchUrl(item.uri)) {
|
||||
if (mounted) {
|
||||
buildSnackbar(
|
||||
context,
|
||||
'Attempting to launch video: ${item.uri}',
|
||||
);
|
||||
}
|
||||
await launchUrl(item.uri);
|
||||
} else {
|
||||
if (mounted) {
|
||||
buildSnackbar(context, 'Unable to launch video: ${item.uri}');
|
||||
}
|
||||
}
|
||||
},
|
||||
child:
|
||||
Text(item.description.isNotEmpty ? item.description : 'Video'));
|
||||
child: SizedBox(
|
||||
height: height,
|
||||
width: width,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Text(
|
||||
item.description.isNotEmpty
|
||||
? item.description
|
||||
: 'Video: ${item.uri}',
|
||||
softWrap: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
if (item.explicitType != AttachmentMediaType.image) {
|
||||
return Text('${item.explicitType}: ${item.uri}');
|
||||
}
|
||||
|
||||
return ImageControl(
|
||||
width: 250.0,
|
||||
height: 250.0,
|
||||
width: width,
|
||||
height: height,
|
||||
imageUrl: item.thumbnailUri.toString(),
|
||||
altText: item.description,
|
||||
onTap: () async {
|
||||
|
|
122
lib/controls/video_control.dart
Normal file
122
lib/controls/video_control.dart
Normal file
|
@ -0,0 +1,122 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import '../services/setting_service.dart';
|
||||
|
||||
final _shownVideos = <String>{};
|
||||
|
||||
class VideoControl extends StatefulWidget {
|
||||
final String videoUrl;
|
||||
final double width;
|
||||
final double height;
|
||||
|
||||
const VideoControl({
|
||||
super.key,
|
||||
required this.videoUrl,
|
||||
required this.width,
|
||||
required this.height,
|
||||
});
|
||||
|
||||
@override
|
||||
State<VideoControl> createState() => _VideoControlState();
|
||||
}
|
||||
|
||||
class _VideoControlState extends State<VideoControl> {
|
||||
late final VideoPlayerController videoPlayerController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
videoPlayerController = VideoPlayerController.network(widget.videoUrl)
|
||||
..initialize().then((_) {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
videoPlayerController.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void deactivate() {
|
||||
videoPlayerController.pause();
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
void showVideo() {
|
||||
_shownVideos.add(widget.videoUrl);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
void toggleVideoPlay() {
|
||||
videoPlayerController.value.isPlaying
|
||||
? videoPlayerController.pause()
|
||||
: videoPlayerController.play();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final shown = !context.watch<SettingsService>().lowBandwidthMode ||
|
||||
_shownVideos.contains(widget.videoUrl);
|
||||
|
||||
final placeHolderBox = SizedBox(
|
||||
width: widget.width,
|
||||
height: widget.height,
|
||||
child: const Card(
|
||||
color: Colors.black12,
|
||||
shape: RoundedRectangleBorder(),
|
||||
child: Icon(Icons.movie)),
|
||||
);
|
||||
|
||||
if (!shown) {
|
||||
return GestureDetector(
|
||||
onTap: showVideo,
|
||||
child: placeHolderBox,
|
||||
);
|
||||
}
|
||||
|
||||
_shownVideos.add(widget.videoUrl);
|
||||
if (!videoPlayerController.value.isInitialized) {
|
||||
return placeHolderBox;
|
||||
}
|
||||
final size = videoPlayerController.value.size;
|
||||
final videoWidth = size.width <= widget.width ? size.width : widget.width;
|
||||
final videoHeight = size.width <= widget.width
|
||||
? size.height
|
||||
: size.height * videoWidth / size.width;
|
||||
return GestureDetector(
|
||||
onTap: toggleVideoPlay,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: videoWidth,
|
||||
height: videoHeight,
|
||||
child: AspectRatio(
|
||||
aspectRatio: videoPlayerController.value.aspectRatio,
|
||||
child: VideoPlayer(videoPlayerController),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: videoPlayerController.value.isPlaying
|
||||
? const Icon(Icons.pause)
|
||||
: const Icon(Icons.play_arrow),
|
||||
onPressed: toggleVideoPlay,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
videoPlayerController.seekTo(Duration.zero);
|
||||
},
|
||||
icon: const Icon(Icons.replay)),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue