Add video player for mobile platforms

This commit is contained in:
Hank Grabowski 2023-01-05 16:17:48 -05:00
parent cd41a0c581
commit 3d7f1b53c0
3 changed files with 177 additions and 11 deletions

View file

@ -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

View file

@ -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 {

View 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)),
],
)
],
),
);
}
}