2021-05-06 13:36:20 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter/widgets.dart';
|
2021-05-09 12:32:03 +02:00
|
|
|
import 'package:logging/logging.dart';
|
2021-05-06 13:36:20 +02:00
|
|
|
import 'package:nc_photos/account.dart';
|
|
|
|
import 'package:nc_photos/api/api.dart';
|
|
|
|
import 'package:nc_photos/api/api_util.dart' as api_util;
|
|
|
|
import 'package:nc_photos/entity/file.dart';
|
|
|
|
import 'package:nc_photos/k.dart' as k;
|
|
|
|
import 'package:nc_photos/widget/animated_visibility.dart';
|
|
|
|
import 'package:video_player/video_player.dart';
|
2021-07-09 13:54:13 +02:00
|
|
|
import 'package:wakelock/wakelock.dart';
|
2021-05-06 13:36:20 +02:00
|
|
|
|
|
|
|
class VideoViewer extends StatefulWidget {
|
|
|
|
VideoViewer({
|
2021-07-23 22:05:57 +02:00
|
|
|
required this.account,
|
|
|
|
required this.file,
|
2021-05-06 13:36:20 +02:00
|
|
|
this.onLoaded,
|
|
|
|
this.onHeightChanged,
|
|
|
|
this.onPlay,
|
|
|
|
this.onPause,
|
|
|
|
this.isControlVisible = false,
|
|
|
|
this.canPlay = true,
|
|
|
|
});
|
|
|
|
|
|
|
|
@override
|
|
|
|
createState() => _VideoViewerState();
|
|
|
|
|
|
|
|
final Account account;
|
|
|
|
final File file;
|
2021-07-23 22:05:57 +02:00
|
|
|
final VoidCallback? onLoaded;
|
|
|
|
final ValueChanged<double>? onHeightChanged;
|
|
|
|
final VoidCallback? onPlay;
|
|
|
|
final VoidCallback? onPause;
|
2021-05-06 13:36:20 +02:00
|
|
|
final bool isControlVisible;
|
|
|
|
final bool canPlay;
|
|
|
|
}
|
|
|
|
|
|
|
|
class _VideoViewerState extends State<VideoViewer> {
|
|
|
|
@override
|
|
|
|
initState() {
|
|
|
|
super.initState();
|
|
|
|
_controller = VideoPlayerController.network(
|
|
|
|
api_util.getFileUrl(widget.account, widget.file),
|
|
|
|
httpHeaders: {
|
|
|
|
"Authorization": Api.getAuthorizationHeaderValue(widget.account),
|
|
|
|
},
|
|
|
|
)..initialize().then((_) {
|
|
|
|
widget.onLoaded?.call();
|
|
|
|
setState(() {});
|
2021-07-23 22:05:57 +02:00
|
|
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
2021-05-06 13:36:20 +02:00
|
|
|
if (_key.currentContext != null) {
|
2021-07-23 22:05:57 +02:00
|
|
|
widget.onHeightChanged?.call(_key.currentContext!.size!.height);
|
2021-05-06 13:36:20 +02:00
|
|
|
}
|
|
|
|
});
|
2021-05-09 12:32:03 +02:00
|
|
|
}).catchError((e, stacktrace) {
|
|
|
|
_log.shout("[initState] Filed while initialize", e, stacktrace);
|
2021-05-06 13:36:20 +02:00
|
|
|
});
|
|
|
|
_controller.addListener(_onControllerChanged);
|
2021-07-09 13:54:13 +02:00
|
|
|
|
|
|
|
Wakelock.enable();
|
2021-05-06 13:36:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
build(BuildContext context) {
|
|
|
|
Widget content;
|
|
|
|
if (_controller.value.isInitialized) {
|
|
|
|
content = _buildPlayer(context);
|
|
|
|
} else {
|
|
|
|
content = Container();
|
|
|
|
}
|
|
|
|
|
|
|
|
return Container(
|
|
|
|
width: MediaQuery.of(context).size.width,
|
|
|
|
height: MediaQuery.of(context).size.height,
|
|
|
|
alignment: Alignment.center,
|
|
|
|
child: content,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
dispose() {
|
|
|
|
super.dispose();
|
2021-07-23 22:05:57 +02:00
|
|
|
_controller.dispose();
|
2021-07-09 13:54:13 +02:00
|
|
|
Wakelock.disable();
|
2021-05-06 13:36:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Widget _buildPlayer(BuildContext context) {
|
|
|
|
if (_controller.value.isPlaying && !widget.canPlay) {
|
2021-07-23 22:05:57 +02:00
|
|
|
WidgetsBinding.instance!.addPostFrameCallback((_) {
|
2021-05-06 13:36:20 +02:00
|
|
|
_pause();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return Stack(
|
|
|
|
children: [
|
|
|
|
Align(
|
|
|
|
alignment: Alignment.center,
|
|
|
|
child: AspectRatio(
|
|
|
|
key: _key,
|
|
|
|
aspectRatio: _controller.value.aspectRatio,
|
|
|
|
child: VideoPlayer(_controller),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Positioned.fill(
|
|
|
|
child: AnimatedVisibility(
|
|
|
|
opacity: widget.isControlVisible ? 1.0 : 0.0,
|
|
|
|
duration: k.animationDurationNormal,
|
|
|
|
child: Container(
|
|
|
|
color: Colors.black45,
|
|
|
|
child: Center(
|
|
|
|
child: IconButton(
|
|
|
|
icon: Icon(_controller.value.isPlaying
|
|
|
|
? Icons.pause_circle_filled
|
|
|
|
: Icons.play_circle_filled),
|
|
|
|
iconSize: 48,
|
|
|
|
padding: const EdgeInsets.all(16),
|
|
|
|
color: Colors.white,
|
|
|
|
onPressed: () => _controller.value.isPlaying
|
|
|
|
? _onPausePressed()
|
|
|
|
: _onPlayPressed(),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Container(
|
|
|
|
child: Align(
|
|
|
|
alignment: Alignment.bottomCenter,
|
|
|
|
child: Padding(
|
|
|
|
padding: EdgeInsets.only(
|
2021-05-06 16:15:00 +02:00
|
|
|
bottom: kToolbarHeight + 8, left: 16, right: 16),
|
2021-05-06 13:36:20 +02:00
|
|
|
child: AnimatedVisibility(
|
|
|
|
opacity: widget.isControlVisible ? 1.0 : 0.0,
|
|
|
|
duration: k.animationDurationNormal,
|
|
|
|
child: VideoProgressIndicator(
|
|
|
|
_controller,
|
|
|
|
allowScrubbing: true,
|
2021-05-06 16:15:00 +02:00
|
|
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
2021-05-06 13:36:20 +02:00
|
|
|
colors: VideoProgressColors(
|
|
|
|
backgroundColor: Colors.white24,
|
|
|
|
bufferedColor: Colors.white38,
|
|
|
|
playedColor: Colors.white,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
void _onPlayPressed() {
|
2021-05-06 16:16:59 +02:00
|
|
|
if (_controller.value.position == _controller.value.duration) {
|
2021-05-06 13:36:20 +02:00
|
|
|
_controller.seekTo(const Duration()).then((_) {
|
|
|
|
setState(() {
|
|
|
|
_play();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
setState(() {
|
|
|
|
_play();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _onPausePressed() {
|
|
|
|
setState(() {
|
|
|
|
_pause();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void _onControllerChanged() {
|
|
|
|
if (!_controller.value.isInitialized) {
|
|
|
|
return;
|
|
|
|
}
|
2021-05-06 16:16:59 +02:00
|
|
|
if (!_isFinished &&
|
|
|
|
_controller.value.position == _controller.value.duration) {
|
|
|
|
_isFinished = true;
|
|
|
|
setState(() {
|
|
|
|
_pause();
|
|
|
|
});
|
2021-05-06 13:36:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _play() {
|
|
|
|
if (widget.canPlay) {
|
2021-05-06 16:16:59 +02:00
|
|
|
_isFinished = false;
|
2021-05-06 13:36:20 +02:00
|
|
|
_controller.play();
|
|
|
|
widget.onPlay?.call();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void _pause() {
|
|
|
|
_controller.pause();
|
|
|
|
widget.onPause?.call();
|
|
|
|
}
|
|
|
|
|
|
|
|
final _key = GlobalKey();
|
2021-07-23 22:05:57 +02:00
|
|
|
late VideoPlayerController _controller;
|
2021-05-06 13:36:20 +02:00
|
|
|
var _isFinished = false;
|
2021-05-09 12:32:03 +02:00
|
|
|
|
2021-06-17 12:59:12 +02:00
|
|
|
static final _log = Logger("widget.video_viewer._VideoViewerState");
|
2021-05-06 13:36:20 +02:00
|
|
|
}
|