feat(mobile-next): 导入官方 openim-flutter-demo 固定基线 3.8.3-patch.3 并完成双构建
- 上游: 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>
This commit is contained in:
@@ -0,0 +1,828 @@
|
||||
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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user