ente/lib/ui/viewer/file/video_controls.dart

311 lines
7.7 KiB
Dart
Raw Normal View History

2020-06-20 23:51:10 +00:00
import 'dart:async';
import 'package:chewie/chewie.dart';
import 'package:chewie/src/material/material_progress_bar.dart';
2020-06-20 23:51:10 +00:00
import 'package:flutter/material.dart';
2022-07-12 06:30:02 +00:00
import 'package:photos/ente_theme_data.dart';
2020-06-20 23:51:10 +00:00
import 'package:photos/utils/date_time_util.dart';
import 'package:video_player/video_player.dart';
class VideoControls extends StatefulWidget {
const VideoControls({Key? key}) : super(key: key);
2020-06-20 23:51:10 +00:00
@override
State<StatefulWidget> createState() {
return _VideoControlsState();
}
}
class _VideoControlsState extends State<VideoControls> {
VideoPlayerValue? _latestValue;
2020-06-20 23:51:10 +00:00
bool _hideStuff = true;
Timer? _hideTimer;
Timer? _initTimer;
Timer? _showAfterExpandCollapseTimer;
2020-06-20 23:51:10 +00:00
bool _dragging = false;
bool _displayTapped = false;
final barHeight = 120.0;
2020-06-20 23:51:10 +00:00
final marginSize = 5.0;
late VideoPlayerController controller;
ChewieController? chewieController;
2020-06-20 23:51:10 +00:00
@override
Widget build(BuildContext context) {
if (_latestValue!.hasError) {
return chewieController!.errorBuilder != null
? chewieController!.errorBuilder!(
2020-06-20 23:51:10 +00:00
context,
chewieController!.videoPlayerController.value.errorDescription!,
2020-06-20 23:51:10 +00:00
)
: Center(
child: Icon(
Icons.error,
2022-03-05 20:52:00 +00:00
color: Theme.of(context).colorScheme.onSurface,
2020-06-20 23:51:10 +00:00
size: 42,
),
);
}
return MouseRegion(
onHover: (_) {
_cancelAndRestartTimer();
},
child: GestureDetector(
onTap: () => _cancelAndRestartTimer(),
child: AbsorbPointer(
absorbing: _hideStuff,
2021-07-11 08:17:44 +00:00
child: Stack(
2020-06-20 23:51:10 +00:00
children: <Widget>[
2021-07-11 08:17:44 +00:00
Column(
children: [
_latestValue != null &&
2022-12-30 15:42:03 +00:00
!_latestValue!.isPlaying &&
_latestValue!.isBuffering
2021-07-11 08:17:44 +00:00
? const Center(
2022-06-09 15:03:08 +00:00
child: CircularProgressIndicator(),
2021-07-11 08:17:44 +00:00
)
: _buildHitArea(),
],
),
Align(
alignment: Alignment.bottomCenter,
child: _buildBottomBar(context),
),
2020-06-20 23:51:10 +00:00
],
),
),
),
);
}
@override
void dispose() {
_dispose();
super.dispose();
}
void _dispose() {
controller.removeListener(_updateState);
_hideTimer?.cancel();
_initTimer?.cancel();
_showAfterExpandCollapseTimer?.cancel();
}
@override
void didChangeDependencies() {
2022-07-12 06:30:02 +00:00
final oldController = chewieController;
2020-06-20 23:51:10 +00:00
chewieController = ChewieController.of(context);
controller = chewieController!.videoPlayerController;
2020-06-20 23:51:10 +00:00
2022-07-12 06:30:02 +00:00
if (oldController != chewieController) {
2020-06-20 23:51:10 +00:00
_dispose();
_initialize();
}
super.didChangeDependencies();
}
2021-07-11 08:17:44 +00:00
Widget _buildBottomBar(
2020-06-20 23:51:10 +00:00
BuildContext context,
) {
2023-08-19 11:39:56 +00:00
final iconColor = Theme.of(context).textTheme.labelLarge!.color;
2020-06-20 23:51:10 +00:00
2021-07-11 08:17:44 +00:00
return Container(
2022-07-04 06:02:17 +00:00
padding: const EdgeInsets.only(bottom: 60),
2021-07-11 08:17:44 +00:00
child: AnimatedOpacity(
opacity: _hideStuff ? 0.0 : 1.0,
2022-07-04 06:02:17 +00:00
duration: const Duration(milliseconds: 300),
2021-07-11 08:17:44 +00:00
child: Container(
height: barHeight,
color: Colors.transparent,
child: Row(
children: <Widget>[
_buildCurrentPosition(iconColor),
chewieController!.isLive ? const SizedBox() : _buildProgressBar(),
2021-07-11 08:17:44 +00:00
_buildTotalDuration(iconColor),
],
),
2020-06-20 23:51:10 +00:00
),
),
);
}
Expanded _buildHitArea() {
return Expanded(
child: GestureDetector(
onTap: () {
if (_latestValue != null) {
if (_displayTapped) {
setState(() {
_hideStuff = true;
});
} else {
2020-06-20 23:51:10 +00:00
_cancelAndRestartTimer();
}
2020-06-20 23:51:10 +00:00
} else {
_playPause();
setState(() {
_hideStuff = true;
});
}
},
child: Container(
color: Colors.transparent,
child: Center(
child: AnimatedOpacity(
2022-07-03 09:49:33 +00:00
opacity:
_latestValue != null && !_hideStuff && !_dragging ? 1.0 : 0.0,
2022-07-04 06:02:17 +00:00
duration: const Duration(milliseconds: 300),
2020-06-20 23:51:10 +00:00
child: GestureDetector(
onTap: _playPause,
child: Padding(
2022-07-04 06:02:17 +00:00
padding: const EdgeInsets.all(12.0),
2020-06-20 23:51:10 +00:00
child: Icon(
_latestValue!.isPlaying ? Icons.pause : Icons.play_arrow,
2022-06-11 08:23:52 +00:00
color: Colors.white, // same for both themes
size: 64.0,
),
2020-06-20 23:51:10 +00:00
),
),
),
),
),
),
);
}
Widget _buildCurrentPosition(Color? iconColor) {
2022-12-30 15:42:03 +00:00
final position =
_latestValue != null ? _latestValue!.position : Duration.zero;
2020-06-20 23:51:10 +00:00
return Container(
2022-07-04 06:02:17 +00:00
margin: const EdgeInsets.only(left: 20.0, right: 16.0),
2020-06-20 23:51:10 +00:00
child: Text(
2022-06-09 15:03:08 +00:00
formatDuration(position),
2022-07-04 06:02:17 +00:00
style: const TextStyle(
2020-06-20 23:51:10 +00:00
fontSize: 12.0,
2022-06-09 15:03:08 +00:00
color: Colors.white,
2020-06-20 23:51:10 +00:00
),
),
);
}
Widget _buildTotalDuration(Color? iconColor) {
2022-12-30 15:42:03 +00:00
final duration =
_latestValue != null ? _latestValue!.duration : Duration.zero;
2020-06-20 23:51:10 +00:00
return Padding(
2022-07-04 06:02:17 +00:00
padding: const EdgeInsets.only(right: 20.0),
2020-06-20 23:51:10 +00:00
child: Text(
2022-06-09 15:03:08 +00:00
formatDuration(duration),
2022-07-04 06:02:17 +00:00
style: const TextStyle(
2020-06-20 23:51:10 +00:00
fontSize: 12.0,
2022-06-09 15:03:08 +00:00
color: Colors.white,
2020-06-20 23:51:10 +00:00
),
),
);
}
void _cancelAndRestartTimer() {
_hideTimer?.cancel();
_startHideTimer();
setState(() {
_hideStuff = false;
_displayTapped = true;
});
}
2022-07-12 06:30:02 +00:00
Future<void> _initialize() async {
2020-06-20 23:51:10 +00:00
controller.addListener(_updateState);
_updateState();
2022-12-30 15:42:03 +00:00
if ((controller.value.isPlaying) || chewieController!.autoPlay) {
2020-06-20 23:51:10 +00:00
_startHideTimer();
}
if (chewieController!.showControlsOnInitialize) {
2022-07-04 06:02:17 +00:00
_initTimer = Timer(const Duration(milliseconds: 200), () {
2020-06-20 23:51:10 +00:00
setState(() {
_hideStuff = false;
});
});
}
}
void _playPause() {
final bool isFinished = _latestValue!.position >= _latestValue!.duration;
2020-06-20 23:51:10 +00:00
setState(() {
if (controller.value.isPlaying) {
_hideStuff = false;
_hideTimer?.cancel();
controller.pause();
} else {
_cancelAndRestartTimer();
2021-03-17 19:19:11 +00:00
if (!controller.value.isInitialized) {
2020-06-20 23:51:10 +00:00
controller.initialize().then((_) {
controller.play();
});
} else {
if (isFinished) {
2022-07-04 06:02:17 +00:00
controller.seekTo(const Duration(seconds: 0));
2020-06-20 23:51:10 +00:00
}
controller.play();
}
}
});
}
void _startHideTimer() {
_hideTimer = Timer(const Duration(seconds: 2), () {
setState(() {
_hideStuff = true;
});
});
}
void _updateState() {
setState(() {
_latestValue = controller.value;
});
}
Widget _buildProgressBar() {
return Expanded(
child: Padding(
2022-07-04 06:02:17 +00:00
padding: const EdgeInsets.only(right: 16.0),
2020-06-20 23:51:10 +00:00
child: MaterialVideoProgressBar(
controller,
onDragStart: () {
setState(() {
_dragging = true;
});
_hideTimer?.cancel();
},
onDragEnd: () {
setState(() {
_dragging = false;
});
_startHideTimer();
},
colors: chewieController!.materialProgressColors ??
2020-06-20 23:51:10 +00:00
ChewieProgressColors(
2022-07-12 06:30:02 +00:00
playedColor: Theme.of(context).colorScheme.greenAlternative,
2022-06-11 08:23:52 +00:00
handleColor: Colors.white,
bufferedColor: Colors.white,
backgroundColor: Theme.of(context).disabledColor,
),
2020-06-20 23:51:10 +00:00
),
),
);
}
}