didvan-app/lib/views/widgets/video/primary_controls.dart

422 lines
13 KiB
Dart

import 'dart:async';
import 'package:animated_custom_dropdown/custom_dropdown.dart';
import 'package:chewie/chewie.dart';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/video/play_btn_animation.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class PrimaryControls extends StatefulWidget {
const PrimaryControls({super.key});
@override
State<PrimaryControls> createState() => _PrimaryControlsState();
}
class _PrimaryControlsState extends State<PrimaryControls> {
late ChewieController chewieController;
bool isAnimating = false;
bool isAnimatingForward = false;
bool isAnimatingBackward = false;
// bool isSpeedMenuOpen = false;
double opacity = 1;
Timer? _hideControlsTimer;
ValueNotifier<Duration> position = ValueNotifier(Duration.zero);
@override
void didChangeDependencies() {
super.didChangeDependencies();
chewieController = ChewieController.of(context);
chewieController.videoPlayerController.addListener(
() {
position.value = chewieController.videoPlayerController.value.position;
},
);
}
void _startHideControlsTimer() {
_hideControlsTimer?.cancel();
_hideControlsTimer = Timer(const Duration(seconds: 5), () {
setState(() {
opacity = 0;
isAnimating = false;
// if (isSpeedMenuOpen) {
// Navigator.pop(context);
// }
});
});
}
@override
void dispose() {
_hideControlsTimer?.cancel(); // Clean up the timer
super.dispose();
}
void _handlePlay() {
{
setState(() {
if (chewieController.isPlaying) {
chewieController.pause();
opacity = 1;
} else {
chewieController.play();
opacity = 0;
}
isAnimating = true;
});
_startHideControlsTimer(); // Restart the timer on tap
}
}
void onClickScreen() {
if (opacity == 0) {
setState(() {
opacity = 1;
});
_startHideControlsTimer(); // Restart the timer on tap
} else {
setState(() {
opacity = 0;
});
}
}
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
child: Stack(
children: [
forwardBtn(),
backwardBtn(),
AnimatedOpacity(
duration: const Duration(milliseconds: 400),
opacity: opacity,
child: Stack(
children: [
seekPositions(),
IgnorePointer(
ignoring: opacity == 0,
child: Stack(
children: [
forground(),
playAndPause(),
],
),
),
],
),
),
],
),
);
}
Positioned seekPositions() {
return Positioned.fill(
child: Row(
children: [
Expanded(
child: InkWell(
onTap: onClickScreen,
onDoubleTap: () async {
if (position.value.inSeconds < 1) return;
setState(() => isAnimatingBackward = true);
Duration backward = Duration(seconds: position.value.inSeconds - 1);
await chewieController.videoPlayerController.seekTo(backward);
},
)),
Expanded(
child: InkWell(
onTap: onClickScreen,
onDoubleTap: () async {
if (position.value.inSeconds >
chewieController
.videoPlayerController.value.duration.inSeconds -
1) return;
setState(() => isAnimatingForward = true);
Duration forward = Duration(seconds: position.value.inSeconds + 1);
await chewieController.videoPlayerController.seekTo(forward);
},
)),
],
));
}
Positioned playAndPause() {
return Positioned.fill(
child: Center(
child: InkWell(
onTap: _handlePlay,
child: Opacity(
opacity: isAnimatingBackward || isAnimatingForward ? 0 : 1,
child: PlayBtnAnimation(
alwaysAnimate: false,
isAnimating: isAnimating,
onEnd: () => setState(
() => isAnimating = false,
),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withValues(alpha: 0.4)),
child: Icon(
chewieController.isPlaying
? CupertinoIcons.pause_fill
: CupertinoIcons.play_fill,
color: Colors.white,
size: 32,
),
),
),
),
)));
}
Positioned forwardBtn() {
return Positioned.fill(
child: Center(
child: Opacity(
opacity: isAnimatingForward ? 1 : 0,
child: PlayBtnAnimation(
alwaysAnimate: false,
isAnimating: isAnimatingForward,
onEnd: () => setState(
() => isAnimatingForward = false,
),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withValues(alpha: 0.4)),
child: const Icon(
Icons.forward_5_rounded,
color: Colors.white,
size: 32,
),
),
),
)));
}
Positioned backwardBtn() {
return Positioned.fill(
child: Center(
child: Opacity(
opacity: isAnimatingBackward ? 1 : 0,
child: PlayBtnAnimation(
alwaysAnimate: false,
isAnimating: isAnimatingBackward,
onEnd: () => setState(
() => isAnimatingBackward = false,
),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withValues(alpha: 0.4)),
child: const Icon(
Icons.replay_5_rounded,
color: Colors.white,
size: 32,
),
),
),
)));
}
Positioned forground() {
return Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.black,
Colors.black87,
Colors.black54,
Colors.black45,
Colors.black26,
Color.fromARGB(10, 0, 0, 0)
])),
child: Row(
children: [
// _buildPlayPause(),
_buildProgressIndicator(),
_buildFullScreenToggle(),
],
),
),
);
}
Positioned speedPlayBackButtons() {
return Positioned(
top: 0,
right: 0,
left: 0,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 12),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black,
Colors.black87,
Colors.black54,
Colors.black45,
Colors.black26,
Color.fromARGB(10, 0, 0, 0)
])),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: 120,
child: CustomDropdown<double>(
closedHeaderPadding: EdgeInsets.zero,
itemsListPadding: EdgeInsets.zero,
items: const [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],
initialItem: 1,
listItemPadding: EdgeInsets.zero,
expandedHeaderPadding: EdgeInsets.zero,
hideSelectedFieldWhenExpanded: false,
// overlayHeight:
// chewieController.isFullScreen ? null : 54 * 8,
decoration: const CustomDropdownDecoration(
closedSuffixIcon: SizedBox(),
closedFillColor: Colors.transparent,
expandedBorderRadius: DesignConfig.lowBorderRadius),
// hintText: "سرعت ویدیو",
listItemBuilder: (context, item, isSelected, onItemSelect) {
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 8),
child: Column(
children: [
DidvanText('x$item'),
if (item != 2)
const DidvanDivider(
verticalPadding: 8,
)
],
));
},
headerBuilder: (context, selectedItem, enabled) => const Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(
Icons.more_vert_rounded,
size: 32,
color: Colors.white,
),
],
),
hintBuilder: (context, hint, enabled) => const SizedBox(),
onChanged: (value) async {
// isSpeedMenuOpen = false;
await chewieController.videoPlayerController
.setPlaybackSpeed(value!);
_startHideControlsTimer();
},
),
),
],
),
));
}
Widget _buildProgressIndicator() {
return Expanded(
child: ValueListenableBuilder<Duration>(
valueListenable: position,
builder: (context, p, _) {
Duration duration =
chewieController.videoPlayerController.value.duration;
return Column(
children: [
SliderTheme(
data: SliderThemeData(
trackHeight: 2,
// thumbColor: Colors.transparent,
overlayShape: SliderComponentShape.noOverlay,
thumbShape: const RoundSliderThumbShape(
// elevation: 0,
// pressedElevation: 0,
enabledThumbRadius: 8)),
child: Slider(
min: 0,
max: duration.inMilliseconds.toDouble(),
value: p.inMilliseconds.toDouble(),
onChanged: (value) async {
await chewieController.pause();
position.value = Duration(milliseconds: value.round());
_startHideControlsTimer();
},
onChangeEnd: (value) async {
await chewieController
.seekTo(Duration(milliseconds: value.round()));
await chewieController.play();
setState(() {});
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DidvanText(
DateTimeUtils.normalizeTimeDuration(p),
color: Colors.white,
fontSize: 16,
),
DidvanText(
DateTimeUtils.normalizeTimeDuration(duration),
color: Colors.white,
fontSize: 16,
)
],
)
],
);
},
),
);
}
Widget _buildFullScreenToggle() {
return Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 8, 12),
child: InkWell(
onTap: () => setState(() {
chewieController.toggleFullScreen();
_startHideControlsTimer(); // Restart the timer on tap
}),
child: Icon(
chewieController.isFullScreen
? Icons.fullscreen_exit
: Icons.fullscreen,
color: Colors.white,
size: 30,
),
),
);
}
}