163 lines
4.0 KiB
Dart
163 lines
4.0 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
|
import 'package:video_player/video_player.dart';
|
|
import '../controller/story_controller.dart';
|
|
import '../utils.dart';
|
|
|
|
class VideoLoader {
|
|
String url;
|
|
|
|
File? videoFile;
|
|
|
|
Map<String, dynamic>? requestHeaders;
|
|
|
|
LoadState state = LoadState.loading;
|
|
|
|
VideoLoader(this.url, {this.requestHeaders});
|
|
|
|
void loadVideo(VoidCallback onComplete) {
|
|
if (videoFile != null) {
|
|
state = LoadState.success;
|
|
onComplete();
|
|
}
|
|
|
|
final fileStream = DefaultCacheManager().getFileStream(url, headers: requestHeaders as Map<String, String>?);
|
|
|
|
fileStream.listen((fileResponse) {
|
|
if (fileResponse is FileInfo) {
|
|
if (videoFile == null) {
|
|
state = LoadState.success;
|
|
videoFile = fileResponse.file;
|
|
onComplete();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
class StoryVideo extends StatefulWidget {
|
|
final StoryController? storyController;
|
|
final VideoLoader videoLoader;
|
|
final Widget? loadingWidget;
|
|
final Widget? errorWidget;
|
|
|
|
StoryVideo(
|
|
this.videoLoader, {
|
|
Key? key,
|
|
this.storyController,
|
|
this.loadingWidget,
|
|
this.errorWidget,
|
|
}) : super(key: key ?? UniqueKey());
|
|
|
|
static StoryVideo url(
|
|
String url, {
|
|
StoryController? controller,
|
|
Map<String, dynamic>? requestHeaders,
|
|
Key? key,
|
|
Widget? loadingWidget,
|
|
Widget? errorWidget,
|
|
}) {
|
|
return StoryVideo(
|
|
VideoLoader(url, requestHeaders: requestHeaders),
|
|
storyController: controller,
|
|
key: key,
|
|
loadingWidget: loadingWidget,
|
|
errorWidget: errorWidget,
|
|
);
|
|
}
|
|
|
|
@override
|
|
State<StatefulWidget> createState() {
|
|
return StoryVideoState();
|
|
}
|
|
}
|
|
|
|
class StoryVideoState extends State<StoryVideo> {
|
|
Future<void>? playerLoader;
|
|
|
|
StreamSubscription? _streamSubscription;
|
|
|
|
VideoPlayerController? playerController;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
widget.storyController!.pause();
|
|
|
|
widget.videoLoader.loadVideo(() {
|
|
if (widget.videoLoader.state == LoadState.success) {
|
|
playerController = VideoPlayerController.file(widget.videoLoader.videoFile!);
|
|
|
|
playerController!.initialize().then((v) {
|
|
setState(() {});
|
|
widget.storyController!.play();
|
|
});
|
|
|
|
if (widget.storyController != null) {
|
|
_streamSubscription = widget.storyController!.playbackNotifier.listen((playbackState) {
|
|
if (playbackState == PlaybackState.pause) {
|
|
playerController!.pause();
|
|
} else {
|
|
playerController!.play();
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
setState(() {});
|
|
}
|
|
});
|
|
}
|
|
|
|
Widget getContentView() {
|
|
if (widget.videoLoader.state == LoadState.success && playerController!.value.isInitialized) {
|
|
return Center(
|
|
child: AspectRatio(
|
|
aspectRatio: playerController!.value.aspectRatio,
|
|
child: VideoPlayer(playerController!),
|
|
),
|
|
);
|
|
}
|
|
|
|
return widget.videoLoader.state == LoadState.loading
|
|
? Center(
|
|
child: widget.loadingWidget ??
|
|
const SizedBox(
|
|
width: 70,
|
|
height: 70,
|
|
child: CircularProgressIndicator(
|
|
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
|
|
strokeWidth: 3,
|
|
),
|
|
),
|
|
)
|
|
: Center(
|
|
child: widget.errorWidget ??
|
|
const Text(
|
|
"Media failed to load.",
|
|
style: TextStyle(
|
|
color: Colors.white,
|
|
),
|
|
));
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
color: Colors.black,
|
|
height: double.infinity,
|
|
width: double.infinity,
|
|
child: getContentView(),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
playerController?.dispose();
|
|
_streamSubscription?.cancel();
|
|
super.dispose();
|
|
}
|
|
}
|