- 上游: github.com/OpenIMSDK/openim-flutter-demo tag 3.8.3-patch.3 commit b3dfdb1e8aaeaaf6f0793e10cadd20d5c184a31f - 并行目录 mobile-next/ 原样导入(含 LICENSE),不覆盖现网 mobile/ - 锁定 Flutter 3.24.5 / Dart 3.5.4 / JDK 17 / Gradle 7.6.3 / AGP 7.3.1 - 实测 Android debug 与 release 构建均成功(见 docs/mobile-next-baseline-import.md) - 本提交可单独回退:git revert 1db1229(重写前) Co-authored-by: multica-agent <github@multica.ai>
829 lines
22 KiB
Dart
829 lines
22 KiB
Dart
import 'dart:async';
|
|
import 'dart:math' as math;
|
|
import 'dart:ui' as ui;
|
|
|
|
import 'package:chewie/src/animated_play_pause.dart';
|
|
import 'package:chewie/src/center_play_button.dart';
|
|
import 'package:chewie/src/chewie_player.dart';
|
|
import 'package:chewie/src/chewie_progress_colors.dart';
|
|
import 'package:chewie/src/cupertino/cupertino_progress_bar.dart';
|
|
import 'package:chewie/src/cupertino/widgets/cupertino_options_dialog.dart';
|
|
import 'package:chewie/src/helpers/utils.dart';
|
|
import 'package:chewie/src/models/option_item.dart';
|
|
import 'package:chewie/src/models/subtitle_model.dart';
|
|
import 'package:chewie/src/notifiers/index.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
import 'package:video_player/video_player.dart';
|
|
|
|
class CustomCupertinoControls extends StatefulWidget {
|
|
const CustomCupertinoControls({
|
|
required this.backgroundColor,
|
|
required this.iconColor,
|
|
this.showPlayButton = true,
|
|
Key? key,
|
|
}) : super(key: key);
|
|
|
|
final Color backgroundColor;
|
|
final Color iconColor;
|
|
final bool showPlayButton;
|
|
|
|
@override
|
|
State<StatefulWidget> createState() {
|
|
return _CustomCupertinoControlsState();
|
|
}
|
|
}
|
|
|
|
class _CustomCupertinoControlsState extends State<CustomCupertinoControls> with SingleTickerProviderStateMixin {
|
|
late PlayerNotifier notifier;
|
|
late VideoPlayerValue _latestValue;
|
|
double? _latestVolume;
|
|
Timer? _hideTimer;
|
|
final marginSize = 5.0;
|
|
Timer? _expandCollapseTimer;
|
|
Timer? _initTimer;
|
|
bool _dragging = false;
|
|
Duration? _subtitlesPosition;
|
|
bool _subtitleOn = false;
|
|
Timer? _bufferingDisplayTimer;
|
|
bool _displayBufferingIndicator = false;
|
|
|
|
late VideoPlayerController controller;
|
|
|
|
ChewieController get chewieController => _chewieController!;
|
|
ChewieController? _chewieController;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
notifier = Provider.of<PlayerNotifier>(context, listen: false);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (_latestValue.hasError) {
|
|
return chewieController.errorBuilder != null
|
|
? chewieController.errorBuilder!(
|
|
context,
|
|
chewieController.videoPlayerController.value.errorDescription!,
|
|
)
|
|
: const Center(
|
|
child: Icon(
|
|
CupertinoIcons.exclamationmark_circle,
|
|
color: Colors.white,
|
|
size: 42,
|
|
),
|
|
);
|
|
}
|
|
|
|
final backgroundColor = widget.backgroundColor;
|
|
final iconColor = widget.iconColor;
|
|
final orientation = MediaQuery.of(context).orientation;
|
|
final barHeight = orientation == Orientation.portrait ? 30.0 : 47.0;
|
|
final buttonPadding = orientation == Orientation.portrait ? 16.0 : 24.0;
|
|
|
|
return MouseRegion(
|
|
onHover: (_) => _cancelAndRestartTimer(),
|
|
child: GestureDetector(
|
|
onTap: () => _cancelAndRestartTimer(),
|
|
child: AbsorbPointer(
|
|
absorbing: notifier.hideStuff,
|
|
child: Stack(
|
|
children: [
|
|
if (_displayBufferingIndicator)
|
|
const Center(
|
|
child: CupertinoActivityIndicator(
|
|
color: Colors.white,
|
|
radius: 15,
|
|
),
|
|
)
|
|
else
|
|
_buildHitArea(),
|
|
Column(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
_buildTopBar(
|
|
backgroundColor,
|
|
iconColor,
|
|
barHeight,
|
|
buttonPadding,
|
|
),
|
|
const Spacer(),
|
|
if (_subtitleOn)
|
|
Transform.translate(
|
|
offset: Offset(
|
|
0.0,
|
|
notifier.hideStuff ? barHeight * 0.8 : 0.0,
|
|
),
|
|
child: _buildSubtitles(chewieController.subtitle!),
|
|
),
|
|
_buildBottomBar(backgroundColor, iconColor, barHeight),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void _dispose() {
|
|
controller.removeListener(_updateState);
|
|
_hideTimer?.cancel();
|
|
_expandCollapseTimer?.cancel();
|
|
_initTimer?.cancel();
|
|
}
|
|
|
|
@override
|
|
void didChangeDependencies() {
|
|
final oldController = _chewieController;
|
|
_chewieController = ChewieController.of(context);
|
|
controller = chewieController.videoPlayerController;
|
|
|
|
if (oldController != chewieController) {
|
|
_dispose();
|
|
_initialize();
|
|
}
|
|
|
|
super.didChangeDependencies();
|
|
}
|
|
|
|
GestureDetector _buildOptionsButton(
|
|
Color iconColor,
|
|
double barHeight,
|
|
) {
|
|
final options = <OptionItem>[];
|
|
|
|
if (chewieController.additionalOptions != null && chewieController.additionalOptions!(context).isNotEmpty) {
|
|
options.addAll(chewieController.additionalOptions!(context));
|
|
}
|
|
|
|
return GestureDetector(
|
|
onTap: () async {
|
|
_hideTimer?.cancel();
|
|
|
|
if (chewieController.optionsBuilder != null) {
|
|
await chewieController.optionsBuilder!(context, options);
|
|
} else {
|
|
await showCupertinoModalPopup<OptionItem>(
|
|
context: context,
|
|
semanticsDismissible: true,
|
|
useRootNavigator: chewieController.useRootNavigator,
|
|
builder: (context) => CupertinoOptionsDialog(
|
|
options: options,
|
|
cancelButtonText: chewieController.optionsTranslation?.cancelButtonText,
|
|
),
|
|
);
|
|
if (_latestValue.isPlaying) {
|
|
_startHideTimer();
|
|
}
|
|
}
|
|
},
|
|
child: Container(
|
|
height: barHeight,
|
|
color: Colors.transparent,
|
|
padding: const EdgeInsets.only(left: 4.0, right: 8.0),
|
|
margin: const EdgeInsets.only(right: 6.0),
|
|
child: Icon(
|
|
Icons.more_vert,
|
|
color: iconColor,
|
|
size: 18,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSubtitles(Subtitles subtitles) {
|
|
if (!_subtitleOn) {
|
|
return const SizedBox();
|
|
}
|
|
if (_subtitlesPosition == null) {
|
|
return const SizedBox();
|
|
}
|
|
final currentSubtitle = subtitles.getByPosition(_subtitlesPosition!);
|
|
if (currentSubtitle.isEmpty) {
|
|
return const SizedBox();
|
|
}
|
|
|
|
if (chewieController.subtitleBuilder != null) {
|
|
return chewieController.subtitleBuilder!(
|
|
context,
|
|
currentSubtitle.first!.text,
|
|
);
|
|
}
|
|
|
|
return Padding(
|
|
padding: EdgeInsets.only(left: marginSize, right: marginSize),
|
|
child: Container(
|
|
padding: const EdgeInsets.all(5),
|
|
decoration: BoxDecoration(
|
|
color: const Color(0x96000000),
|
|
borderRadius: BorderRadius.circular(10.0),
|
|
),
|
|
child: Text(
|
|
currentSubtitle.first!.text.toString(),
|
|
style: const TextStyle(
|
|
fontSize: 18,
|
|
),
|
|
textAlign: TextAlign.center,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildBottomBar(
|
|
Color backgroundColor,
|
|
Color iconColor,
|
|
double barHeight,
|
|
) {
|
|
return SafeArea(
|
|
bottom: chewieController.isFullScreen,
|
|
minimum: chewieController.controlsSafeAreaMinimum,
|
|
child: AnimatedOpacity(
|
|
opacity: notifier.hideStuff ? 0.0 : 1.0,
|
|
duration: const Duration(milliseconds: 300),
|
|
child: Container(
|
|
color: Colors.transparent,
|
|
alignment: Alignment.bottomCenter,
|
|
margin: EdgeInsets.all(marginSize),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(10.0),
|
|
child: BackdropFilter(
|
|
filter: ui.ImageFilter.blur(
|
|
sigmaX: 10.0,
|
|
sigmaY: 10.0,
|
|
),
|
|
child: Container(
|
|
height: barHeight,
|
|
color: backgroundColor,
|
|
child: chewieController.isLive
|
|
? Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: <Widget>[
|
|
_buildPlayPause(controller, iconColor, barHeight),
|
|
_buildLive(iconColor),
|
|
],
|
|
)
|
|
: Row(
|
|
children: <Widget>[
|
|
_buildPlayPause(controller, iconColor, barHeight),
|
|
_buildPosition(iconColor),
|
|
_buildProgressBar(),
|
|
_buildRemaining(iconColor),
|
|
_buildSubtitleToggle(iconColor, barHeight),
|
|
if (chewieController.allowPlaybackSpeedChanging)
|
|
_buildSpeedButton(controller, iconColor, barHeight),
|
|
if (chewieController.additionalOptions != null &&
|
|
chewieController.additionalOptions!(context).isNotEmpty)
|
|
_buildOptionsButton(iconColor, barHeight),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildLive(Color iconColor) {
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 12.0),
|
|
child: Text(
|
|
'LIVE',
|
|
style: TextStyle(color: iconColor, fontSize: 12.0),
|
|
),
|
|
);
|
|
}
|
|
|
|
GestureDetector _buildExpandButton(
|
|
Color backgroundColor,
|
|
Color iconColor,
|
|
double barHeight,
|
|
double buttonPadding,
|
|
) {
|
|
return GestureDetector(
|
|
onTap: _onExpandCollapse,
|
|
child: AnimatedOpacity(
|
|
opacity: notifier.hideStuff ? 0.0 : 1.0,
|
|
duration: const Duration(milliseconds: 300),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(10.0),
|
|
child: BackdropFilter(
|
|
filter: ui.ImageFilter.blur(sigmaX: 10.0),
|
|
child: Container(
|
|
height: barHeight,
|
|
padding: EdgeInsets.only(
|
|
left: buttonPadding,
|
|
right: buttonPadding,
|
|
),
|
|
color: backgroundColor,
|
|
child: Center(
|
|
child: Icon(
|
|
chewieController.isFullScreen
|
|
? CupertinoIcons.arrow_down_right_arrow_up_left
|
|
: CupertinoIcons.arrow_up_left_arrow_down_right,
|
|
color: iconColor,
|
|
size: 16,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildHitArea() {
|
|
final bool isFinished = _latestValue.position >= _latestValue.duration;
|
|
final bool showPlayButton = widget.showPlayButton && !_latestValue.isPlaying && !_dragging;
|
|
|
|
return GestureDetector(
|
|
onTap: _latestValue.isPlaying
|
|
? _cancelAndRestartTimer
|
|
: () {
|
|
_hideTimer?.cancel();
|
|
|
|
setState(() {
|
|
notifier.hideStuff = false;
|
|
});
|
|
},
|
|
child: CenterPlayButton(
|
|
backgroundColor: widget.backgroundColor,
|
|
iconColor: widget.iconColor,
|
|
isFinished: isFinished,
|
|
isPlaying: controller.value.isPlaying,
|
|
show: showPlayButton,
|
|
onPressed: _playPause,
|
|
),
|
|
);
|
|
}
|
|
|
|
GestureDetector _buildMuteButton(
|
|
VideoPlayerController controller,
|
|
Color backgroundColor,
|
|
Color iconColor,
|
|
double barHeight,
|
|
double buttonPadding,
|
|
) {
|
|
return GestureDetector(
|
|
onTap: () {
|
|
_cancelAndRestartTimer();
|
|
|
|
if (_latestValue.volume == 0) {
|
|
controller.setVolume(_latestVolume ?? 0.5);
|
|
} else {
|
|
_latestVolume = controller.value.volume;
|
|
controller.setVolume(0.0);
|
|
}
|
|
},
|
|
child: AnimatedOpacity(
|
|
opacity: notifier.hideStuff ? 0.0 : 1.0,
|
|
duration: const Duration(milliseconds: 300),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(10.0),
|
|
child: BackdropFilter(
|
|
filter: ui.ImageFilter.blur(sigmaX: 10.0),
|
|
child: ColoredBox(
|
|
color: backgroundColor,
|
|
child: Container(
|
|
height: barHeight,
|
|
padding: EdgeInsets.only(
|
|
left: buttonPadding,
|
|
right: buttonPadding,
|
|
),
|
|
child: Icon(
|
|
_latestValue.volume > 0 ? Icons.volume_up : Icons.volume_off,
|
|
color: iconColor,
|
|
size: 16,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
GestureDetector _buildPlayPause(
|
|
VideoPlayerController controller,
|
|
Color iconColor,
|
|
double barHeight,
|
|
) {
|
|
return GestureDetector(
|
|
onTap: _playPause,
|
|
child: Container(
|
|
height: barHeight,
|
|
color: Colors.transparent,
|
|
padding: const EdgeInsets.only(
|
|
left: 6.0,
|
|
right: 6.0,
|
|
),
|
|
child: AnimatedPlayPause(
|
|
color: widget.iconColor,
|
|
playing: controller.value.isPlaying,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildPosition(Color iconColor) {
|
|
final position = _latestValue.position;
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 12.0),
|
|
child: Text(
|
|
formatDuration(position),
|
|
style: TextStyle(
|
|
color: iconColor,
|
|
fontSize: 12.0,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildRemaining(Color iconColor) {
|
|
final position = _latestValue.duration - _latestValue.position;
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.only(right: 12.0),
|
|
child: Text(
|
|
'-${formatDuration(position)}',
|
|
style: TextStyle(color: iconColor, fontSize: 12.0),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildSubtitleToggle(Color iconColor, double barHeight) {
|
|
if (chewieController.subtitle?.isEmpty ?? true) {
|
|
return const SizedBox();
|
|
}
|
|
return GestureDetector(
|
|
onTap: _subtitleToggle,
|
|
child: Container(
|
|
height: barHeight,
|
|
color: Colors.transparent,
|
|
margin: const EdgeInsets.only(right: 10.0),
|
|
padding: const EdgeInsets.only(
|
|
left: 6.0,
|
|
right: 6.0,
|
|
),
|
|
child: Icon(
|
|
Icons.subtitles,
|
|
color: _subtitleOn ? iconColor : Colors.grey[700],
|
|
size: 16.0,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _subtitleToggle() {
|
|
setState(() {
|
|
_subtitleOn = !_subtitleOn;
|
|
});
|
|
}
|
|
|
|
GestureDetector _buildSkipBack(Color iconColor, double barHeight) {
|
|
return GestureDetector(
|
|
onTap: _skipBack,
|
|
child: Container(
|
|
height: barHeight,
|
|
color: Colors.transparent,
|
|
margin: const EdgeInsets.only(left: 10.0),
|
|
padding: const EdgeInsets.only(
|
|
left: 6.0,
|
|
right: 6.0,
|
|
),
|
|
child: Icon(
|
|
CupertinoIcons.gobackward_15,
|
|
color: iconColor,
|
|
size: 18.0,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
GestureDetector _buildSkipForward(Color iconColor, double barHeight) {
|
|
return GestureDetector(
|
|
onTap: _skipForward,
|
|
child: Container(
|
|
height: barHeight,
|
|
color: Colors.transparent,
|
|
padding: const EdgeInsets.only(
|
|
left: 6.0,
|
|
right: 8.0,
|
|
),
|
|
margin: const EdgeInsets.only(
|
|
right: 8.0,
|
|
),
|
|
child: Icon(
|
|
CupertinoIcons.goforward_15,
|
|
color: iconColor,
|
|
size: 18.0,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
GestureDetector _buildSpeedButton(
|
|
VideoPlayerController controller,
|
|
Color iconColor,
|
|
double barHeight,
|
|
) {
|
|
return GestureDetector(
|
|
onTap: () async {
|
|
_hideTimer?.cancel();
|
|
|
|
final chosenSpeed = await showCupertinoModalPopup<double>(
|
|
context: context,
|
|
semanticsDismissible: true,
|
|
useRootNavigator: chewieController.useRootNavigator,
|
|
builder: (context) => _PlaybackSpeedDialog(
|
|
speeds: chewieController.playbackSpeeds,
|
|
selected: _latestValue.playbackSpeed,
|
|
),
|
|
);
|
|
|
|
if (chosenSpeed != null) {
|
|
controller.setPlaybackSpeed(chosenSpeed);
|
|
}
|
|
|
|
if (_latestValue.isPlaying) {
|
|
_startHideTimer();
|
|
}
|
|
},
|
|
child: Container(
|
|
height: barHeight,
|
|
color: Colors.transparent,
|
|
padding: const EdgeInsets.only(
|
|
left: 6.0,
|
|
right: 8.0,
|
|
),
|
|
margin: const EdgeInsets.only(
|
|
right: 8.0,
|
|
),
|
|
child: Transform(
|
|
alignment: Alignment.center,
|
|
transform: Matrix4.skewY(0.0)
|
|
..rotateX(math.pi)
|
|
..rotateZ(math.pi * 0.8),
|
|
child: Icon(
|
|
Icons.speed,
|
|
color: iconColor,
|
|
size: 18.0,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildTopBar(
|
|
Color backgroundColor,
|
|
Color iconColor,
|
|
double barHeight,
|
|
double buttonPadding,
|
|
) {
|
|
return Container(
|
|
height: barHeight,
|
|
margin: EdgeInsets.only(
|
|
top: marginSize,
|
|
right: marginSize,
|
|
left: marginSize,
|
|
),
|
|
child: Row(
|
|
children: <Widget>[
|
|
if (chewieController.allowFullScreen)
|
|
_buildExpandButton(
|
|
backgroundColor,
|
|
iconColor,
|
|
barHeight,
|
|
buttonPadding,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
void _cancelAndRestartTimer() {
|
|
_hideTimer?.cancel();
|
|
|
|
setState(() {
|
|
notifier.hideStuff = false;
|
|
|
|
_startHideTimer();
|
|
});
|
|
}
|
|
|
|
Future<void> _initialize() async {
|
|
_subtitleOn = chewieController.subtitle?.isNotEmpty ?? false;
|
|
controller.addListener(_updateState);
|
|
|
|
_updateState();
|
|
|
|
if (controller.value.isPlaying || chewieController.autoPlay) {
|
|
_startHideTimer();
|
|
}
|
|
|
|
if (chewieController.showControlsOnInitialize) {
|
|
_initTimer = Timer(const Duration(milliseconds: 200), () {
|
|
setState(() {
|
|
notifier.hideStuff = false;
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
void _onExpandCollapse() {
|
|
setState(() {
|
|
notifier.hideStuff = true;
|
|
|
|
chewieController.toggleFullScreen();
|
|
_expandCollapseTimer = Timer(const Duration(milliseconds: 300), () {
|
|
setState(() {
|
|
_cancelAndRestartTimer();
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
Widget _buildProgressBar() {
|
|
return Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(right: 12.0),
|
|
child: CupertinoVideoProgressBar(
|
|
controller,
|
|
onDragStart: () {
|
|
setState(() {
|
|
_dragging = true;
|
|
});
|
|
|
|
_hideTimer?.cancel();
|
|
},
|
|
onDragUpdate: () {
|
|
_hideTimer?.cancel();
|
|
},
|
|
onDragEnd: () {
|
|
setState(() {
|
|
_dragging = false;
|
|
});
|
|
|
|
_startHideTimer();
|
|
},
|
|
colors: chewieController.cupertinoProgressColors ??
|
|
ChewieProgressColors(
|
|
playedColor: const Color.fromARGB(
|
|
120,
|
|
255,
|
|
255,
|
|
255,
|
|
),
|
|
handleColor: const Color.fromARGB(
|
|
255,
|
|
255,
|
|
255,
|
|
255,
|
|
),
|
|
bufferedColor: const Color.fromARGB(
|
|
60,
|
|
255,
|
|
255,
|
|
255,
|
|
),
|
|
backgroundColor: const Color.fromARGB(
|
|
20,
|
|
255,
|
|
255,
|
|
255,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _playPause() {
|
|
final isFinished = _latestValue.position >= _latestValue.duration;
|
|
|
|
setState(() {
|
|
if (controller.value.isPlaying) {
|
|
notifier.hideStuff = false;
|
|
_hideTimer?.cancel();
|
|
controller.pause();
|
|
} else {
|
|
_cancelAndRestartTimer();
|
|
|
|
if (!controller.value.isInitialized) {
|
|
controller.initialize().then((_) {
|
|
controller.play();
|
|
});
|
|
} else {
|
|
if (isFinished) {
|
|
controller.seekTo(Duration.zero);
|
|
}
|
|
controller.play();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
void _skipBack() {
|
|
_cancelAndRestartTimer();
|
|
final beginning = Duration.zero.inMilliseconds;
|
|
final skip = (_latestValue.position - const Duration(seconds: 15)).inMilliseconds;
|
|
controller.seekTo(Duration(milliseconds: math.max(skip, beginning)));
|
|
}
|
|
|
|
void _skipForward() {
|
|
_cancelAndRestartTimer();
|
|
final end = _latestValue.duration.inMilliseconds;
|
|
final skip = (_latestValue.position + const Duration(seconds: 15)).inMilliseconds;
|
|
controller.seekTo(Duration(milliseconds: math.min(skip, end)));
|
|
}
|
|
|
|
void _startHideTimer() {
|
|
final hideControlsTimer = chewieController.hideControlsTimer.isNegative
|
|
? ChewieController.defaultHideControlsTimer
|
|
: chewieController.hideControlsTimer;
|
|
_hideTimer = Timer(hideControlsTimer, () {
|
|
setState(() {
|
|
notifier.hideStuff = true;
|
|
});
|
|
});
|
|
}
|
|
|
|
void _bufferingTimerTimeout() {
|
|
_displayBufferingIndicator = true;
|
|
if (mounted) {
|
|
setState(() {});
|
|
}
|
|
}
|
|
|
|
void _updateState() {
|
|
if (!mounted) return;
|
|
|
|
if (chewieController.progressIndicatorDelay != null) {
|
|
if (controller.value.isBuffering) {
|
|
_bufferingDisplayTimer ??= Timer(
|
|
chewieController.progressIndicatorDelay!,
|
|
_bufferingTimerTimeout,
|
|
);
|
|
} else {
|
|
_bufferingDisplayTimer?.cancel();
|
|
_bufferingDisplayTimer = null;
|
|
_displayBufferingIndicator = false;
|
|
}
|
|
} else {
|
|
_displayBufferingIndicator = controller.value.isBuffering;
|
|
}
|
|
|
|
setState(() {
|
|
_latestValue = controller.value;
|
|
_subtitlesPosition = controller.value.position;
|
|
});
|
|
}
|
|
}
|
|
|
|
class _PlaybackSpeedDialog extends StatelessWidget {
|
|
const _PlaybackSpeedDialog({
|
|
Key? key,
|
|
required List<double> speeds,
|
|
required double selected,
|
|
}) : _speeds = speeds,
|
|
_selected = selected,
|
|
super(key: key);
|
|
|
|
final List<double> _speeds;
|
|
final double _selected;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final selectedColor = CupertinoTheme.of(context).primaryColor;
|
|
|
|
return CupertinoActionSheet(
|
|
actions: _speeds
|
|
.map(
|
|
(e) => CupertinoActionSheetAction(
|
|
onPressed: () {
|
|
Navigator.of(context).pop(e);
|
|
},
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
if (e == _selected) Icon(Icons.check, size: 20.0, color: selectedColor),
|
|
Text(e.toString()),
|
|
],
|
|
),
|
|
),
|
|
)
|
|
.toList(),
|
|
);
|
|
}
|
|
}
|