812 lines
30 KiB
Dart
812 lines
30 KiB
Dart
import 'dart:ui';
|
||
|
||
import 'package:easy_debounce/easy_throttle.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_kinetra/kt_utils/kt_string_extend.dart';
|
||
import 'package:flutter_kinetra/kt_utils/kt_utils.dart';
|
||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||
import 'package:get/get.dart';
|
||
import 'package:video_player/video_player.dart';
|
||
import 'package:visibility_detector/visibility_detector.dart';
|
||
|
||
import '../../kt_utils/kt_toast_utils.dart';
|
||
import '../../kt_widgets/kt_network_image.dart';
|
||
import '../../kt_widgets/kt_status_widget.dart';
|
||
import '../../kt_widgets/kt_video_progress_bar.dart';
|
||
import 'logic.dart';
|
||
|
||
class VideoPlayPage extends StatefulWidget {
|
||
const VideoPlayPage({super.key});
|
||
|
||
@override
|
||
State<VideoPlayPage> createState() => _VideoPlayPageState();
|
||
}
|
||
|
||
class _VideoPlayPageState extends State<VideoPlayPage>
|
||
with SingleTickerProviderStateMixin {
|
||
final logic = Get.put(VideoPlayLogic());
|
||
final state = Get.find<VideoPlayLogic>().state;
|
||
|
||
@override
|
||
void initState() {
|
||
state.imageUrl = Get.arguments['imageUrl'] ?? '';
|
||
super.initState();
|
||
}
|
||
|
||
// 选择倍速
|
||
void _selectSpeed(double speed) {
|
||
state.currentSpeed = speed;
|
||
logic.controllers[logic.currentIndex]?.setPlaybackSpeed(speed);
|
||
}
|
||
|
||
bool get isPause =>
|
||
!((logic.controllers[logic.currentIndex]?.value.isPlaying ?? true) ||
|
||
!(logic.controllers[logic.currentIndex]?.value.isBuffering ?? true));
|
||
|
||
bool get isAllOver =>
|
||
logic.currentIndex == state.episodeList.length - 1 &&
|
||
(logic.controllers[logic.currentIndex]?.value.isCompleted ?? false);
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return PopScope(
|
||
canPop: true,
|
||
onPopInvokedWithResult: (didPop, result) async {
|
||
// if (logic.controllers.isNotEmpty) {
|
||
// logic.uploadHistorySeconds(logic.controllers[logic.currentIndex]?.value.position.inMilliseconds ?? 0);
|
||
// }
|
||
if (didPop) return;
|
||
},
|
||
child: Scaffold(
|
||
backgroundColor: Colors.white,
|
||
body: GetBuilder<VideoPlayLogic>(
|
||
assignId: true,
|
||
builder: (ctrl) {
|
||
if (state.loadStatus == KtLoadStatusType.loadFailed) {
|
||
return SizedBox(
|
||
width: ScreenUtil().screenWidth,
|
||
height: ScreenUtil().screenHeight,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Container(
|
||
margin: EdgeInsets.only(
|
||
top: ScreenUtil().statusBarHeight,
|
||
),
|
||
child: IconButton(
|
||
onPressed: () => Get.back(),
|
||
icon: Icon(Icons.arrow_back_outlined, size: 28),
|
||
),
|
||
),
|
||
SizedBox(height: 100.w),
|
||
KtStatusWidget(
|
||
type: KtErrorStatusType.loadFailed,
|
||
onPressed: logic.fetchData,
|
||
),
|
||
],
|
||
),
|
||
);
|
||
}
|
||
return Stack(
|
||
children: [
|
||
KtNetworkImage(
|
||
imageUrl: state.imageUrl,
|
||
width: ScreenUtil().screenWidth,
|
||
height: ScreenUtil().screenHeight,
|
||
placeholder: Image.asset(
|
||
'bg2.png'.ktIcon,
|
||
width: ScreenUtil().screenWidth,
|
||
height: ScreenUtil().screenHeight,
|
||
fit: BoxFit.cover,
|
||
),
|
||
),
|
||
BackdropFilter(
|
||
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
|
||
child: Container(
|
||
color: Colors.black.withAlpha(30),
|
||
width: ScreenUtil().screenWidth,
|
||
height: ScreenUtil().screenHeight,
|
||
child: Center(child: CircularProgressIndicator()),
|
||
),
|
||
),
|
||
|
||
// if (state.video == null) Center(child: CircularProgressIndicator(color: DrColors.mainColor)),
|
||
PageView.builder(
|
||
controller: logic.pageController,
|
||
scrollDirection: Axis.vertical,
|
||
onPageChanged: (index) => logic.onPageChanged(index, type: 1),
|
||
itemCount: state.episodeList.length,
|
||
itemBuilder: (context, index) => _videoPlayWidget(index),
|
||
),
|
||
],
|
||
);
|
||
},
|
||
),
|
||
),
|
||
);
|
||
}
|
||
|
||
Widget _videoPlayWidget(int index) {
|
||
if (logic.controllers.isEmpty) return Container();
|
||
|
||
final controller = logic.controllers[index];
|
||
final episode = state.episodeList[index];
|
||
return Stack(
|
||
children: [
|
||
// 视频播放器
|
||
if (controller != null && controller.value.isInitialized && !isAllOver)
|
||
GestureDetector(
|
||
onTap: () {
|
||
if (episode.isLock == true) return;
|
||
controller.value.isPlaying
|
||
? controller.pause()
|
||
: controller.play();
|
||
setState(() {});
|
||
},
|
||
child: Stack(
|
||
alignment: Alignment.center,
|
||
children: [
|
||
Positioned.fill(
|
||
child: FittedBox(
|
||
fit: BoxFit.cover,
|
||
child: VisibilityDetector(
|
||
key: Key('short-video-$index'),
|
||
onVisibilityChanged: (VisibilityInfo info) {
|
||
var visiblePercentage = info.visibleFraction * 100;
|
||
if (visiblePercentage > 20) {
|
||
if (episode.isLock == true) return;
|
||
controller.play();
|
||
} else {
|
||
controller.pause();
|
||
}
|
||
logic.update();
|
||
},
|
||
child: SizedBox(
|
||
width: controller.value.size.width,
|
||
height: controller.value.size.height,
|
||
child: VideoPlayer(controller),
|
||
),
|
||
),
|
||
),
|
||
),
|
||
|
||
// if (logic.loadStatusType == LoadStatusType.loading)
|
||
// Center(child: CircularProgressIndicator(color: DrColors.mainColor)),
|
||
if (!controller.value.isPlaying)
|
||
// if (!controller.value.isPlaying && logic.loadStatusType == LoadStatusType.loadSuccess)
|
||
Center(
|
||
child: Image.asset(
|
||
'ic_play.png'.ktIcon,
|
||
width: 62.w,
|
||
height: 62.w,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
)
|
||
else
|
||
Stack(
|
||
children: [
|
||
KtNetworkImage(
|
||
width: ScreenUtil().screenWidth,
|
||
height: ScreenUtil().screenHeight,
|
||
imageUrl: state.imageUrl,
|
||
placeholder: Image.asset(
|
||
'bg2.png'.ktIcon,
|
||
width: ScreenUtil().screenWidth,
|
||
height: ScreenUtil().screenHeight,
|
||
fit: BoxFit.cover,
|
||
),
|
||
),
|
||
if (!isAllOver) Center(child: CircularProgressIndicator()),
|
||
],
|
||
),
|
||
|
||
if (episode.isLock == true)
|
||
Stack(
|
||
children: [
|
||
KtNetworkImage(
|
||
width: ScreenUtil().screenWidth,
|
||
height: ScreenUtil().screenHeight,
|
||
imageUrl: state.imageUrl,
|
||
placeholder: Image.asset(
|
||
'bg2.png'.ktIcon,
|
||
width: ScreenUtil().screenWidth,
|
||
height: ScreenUtil().screenHeight,
|
||
fit: BoxFit.cover,
|
||
),
|
||
),
|
||
Container(
|
||
width: ScreenUtil().screenWidth,
|
||
height: ScreenUtil().screenHeight,
|
||
color: Colors.black.withValues(alpha: .75),
|
||
child: Column(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Image.asset('lock_white.png'.ktIcon, width: 50.w),
|
||
SizedBox(height: 26.w),
|
||
GestureDetector(
|
||
onTap: () {
|
||
EasyThrottle.throttle(
|
||
'unlock',
|
||
Duration(seconds: 2),
|
||
() => logic.buyVideo(
|
||
episode.id!,
|
||
episode.coins ?? 0,
|
||
toRecharge: true,
|
||
),
|
||
);
|
||
},
|
||
child: Container(
|
||
width: 280.w,
|
||
padding: EdgeInsets.only(top: 17.w, bottom: 28.w),
|
||
decoration: BoxDecoration(
|
||
image: DecorationImage(
|
||
image: AssetImage('unlock_bg.png'.ktIcon),
|
||
fit: BoxFit.fill,
|
||
),
|
||
),
|
||
child: Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Image.asset('lock_black.png'.ktIcon, width: 20.w),
|
||
SizedBox(width: 3.w),
|
||
Text(
|
||
state.curUnlock == index
|
||
? 'Unlocking costs ${episode.coins ?? 0}'
|
||
: 'Prev.locked',
|
||
style: TextStyle(
|
||
fontSize: 14.sp,
|
||
color: Color(0xFF1E1E20),
|
||
),
|
||
),
|
||
Image.asset('ic_coin.png'.ktIcon, width: 18.7.w),
|
||
|
||
],
|
||
),
|
||
),
|
||
),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.center,
|
||
children: [
|
||
Text(
|
||
"Balance:${logic.userLogic.state.userInfo.coinLeftTotal ?? 0} ",
|
||
style: TextStyle(
|
||
fontSize: 12.sp,
|
||
color: Color(0xFFF5F5F5),
|
||
),
|
||
),
|
||
Image.asset('ic_coin.png'.ktIcon, width: 11.2.w),
|
||
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
// 顶部返回
|
||
Positioned(
|
||
top: ScreenUtil().statusBarHeight + 10.w,
|
||
left: 16.w,
|
||
child: GestureDetector(
|
||
onTap: () {
|
||
// if (state.isFromRecommend || state.recommendList.isEmpty) {
|
||
Get.back();
|
||
// } else {
|
||
// logic.showRecommendDialog();
|
||
// }
|
||
},
|
||
child: Image.asset('ic_back_white.png'.ktIcon, width: 24.w),
|
||
),
|
||
),
|
||
// 底部控制器
|
||
Positioned(
|
||
bottom: 0,
|
||
left: 0.w,
|
||
child: Container(
|
||
width: ScreenUtil().screenWidth,
|
||
padding: EdgeInsets.fromLTRB(15.w, 40.w, 15.w, 40.w),
|
||
alignment: Alignment.bottomCenter,
|
||
decoration: BoxDecoration(
|
||
gradient: LinearGradient(
|
||
begin: Alignment.topCenter,
|
||
end: Alignment.bottomCenter,
|
||
colors: [
|
||
Color(0xFF001D1F).withValues(alpha: 0),
|
||
Color(0xFF001D1F),
|
||
],
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
SizedBox(
|
||
width: ScreenUtil().screenWidth - 100.w,
|
||
child: Text(
|
||
state.video?.shortPlayInfo?.name ?? '',
|
||
maxLines: 1,
|
||
overflow: TextOverflow.ellipsis,
|
||
style: TextStyle(
|
||
fontSize: 16.sp,
|
||
color: Colors.white,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
),
|
||
if (logic.controllers[index] != null)
|
||
CustomVideoProgressBar(
|
||
controller: logic.controllers[index]!,
|
||
width: ScreenUtil().screenWidth,
|
||
),
|
||
SizedBox(height: 16.w),
|
||
Row(
|
||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||
children: [
|
||
GestureDetector(
|
||
onTap: showEpSelDialog,
|
||
child: Container(
|
||
width: 300.w,
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 12.w,
|
||
vertical: 9.w,
|
||
),
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(50.w),
|
||
color: Colors.white.withValues(alpha: .1),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Image.asset('ic_ep.png'.ktIcon, width: 16.sp),
|
||
SizedBox(width: 6.w),
|
||
Text(
|
||
'EP.${index + 1}',
|
||
style: TextStyle(
|
||
fontSize: 13.sp,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
const Spacer(),
|
||
Text(
|
||
'All ${state.video?.shortPlayInfo?.episodeTotal ?? 0} Episodes',
|
||
style: TextStyle(
|
||
fontSize: 13.sp,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
GetBuilder<VideoPlayLogic>(
|
||
id: 'video-like',
|
||
builder: (c) {
|
||
return GestureDetector(
|
||
onTap: () => logic.likeVideo(),
|
||
child: Column(
|
||
children: [
|
||
Image.asset(
|
||
state.video?.shortPlayInfo?.isCollect == true
|
||
? 'ic_collect_sel.png'.ktIcon
|
||
: 'ic_collect_unsel.png'.ktIcon,
|
||
width: 32.w,
|
||
),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
showFeatureDialog() {
|
||
return showModalBottomSheet(
|
||
context: context,
|
||
isScrollControlled: true,
|
||
builder: (context) => StatefulBuilder(
|
||
builder: (context, dialogState) {
|
||
return Container(
|
||
height: 245.w,
|
||
padding: EdgeInsets.symmetric(horizontal: 16.w),
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.vertical(top: Radius.circular(12.w)),
|
||
image: DecorationImage(
|
||
image: AssetImage('ic_speed_bg.png'.ktIcon),
|
||
fit: BoxFit.fill,
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
SizedBox(height: 36.w),
|
||
GestureDetector(
|
||
onTap: () {
|
||
Get.back();
|
||
showSpeedSelDialog();
|
||
},
|
||
child: Container(
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 12.w,
|
||
vertical: 10.w,
|
||
),
|
||
decoration: BoxDecoration(
|
||
gradient: LinearGradient(
|
||
colors: [
|
||
Color(0xFF03011B).withValues(alpha: .4),
|
||
Color(0xFF00000B).withValues(alpha: .4),
|
||
],
|
||
),
|
||
borderRadius: BorderRadius.circular(8.w),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
margin: EdgeInsets.only(right: 5.w),
|
||
child: Image.asset(
|
||
'ic_cur_speed.png'.ktIcon,
|
||
width: 24.w,
|
||
),
|
||
),
|
||
Text(
|
||
'Playback Speed',
|
||
style: TextStyle(
|
||
fontSize: 12.sp,
|
||
color: Colors.white,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
const Spacer(),
|
||
Text(
|
||
'${state.currentSpeed}x',
|
||
style: TextStyle(
|
||
fontSize: 12.sp,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
Container(
|
||
margin: EdgeInsets.only(left: 9.w),
|
||
child: Image.asset(
|
||
'ic_right_grey.png'.ktIcon,
|
||
width: 8.w,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
SizedBox(height: 18.w),
|
||
GestureDetector(
|
||
onTap: () {
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
padding: EdgeInsets.symmetric(
|
||
horizontal: 12.w,
|
||
vertical: 10.w,
|
||
),
|
||
decoration: BoxDecoration(
|
||
gradient: LinearGradient(
|
||
colors: [
|
||
Color(0xFF03011B).withValues(alpha: .4),
|
||
Color(0xFF00000B).withValues(alpha: .4),
|
||
],
|
||
),
|
||
borderRadius: BorderRadius.circular(8.w),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Container(
|
||
margin: EdgeInsets.only(right: 5.w),
|
||
child: Image.asset(
|
||
'ic_video_feedback.png'.ktIcon,
|
||
width: 16.w,
|
||
),
|
||
),
|
||
Text(
|
||
'Feedback',
|
||
style: TextStyle(
|
||
fontSize: 12.sp,
|
||
color: Colors.white,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
const Spacer(),
|
||
Container(
|
||
margin: EdgeInsets.only(left: 9.w),
|
||
child: Image.asset(
|
||
'ic_right_grey.png'.ktIcon,
|
||
width: 8.w,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
|
||
showEpSelDialog() {
|
||
return Get.bottomSheet(
|
||
Container(
|
||
height: 550.w,
|
||
padding: EdgeInsets.symmetric(horizontal: 15.w, vertical: 10.w),
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.vertical(top: Radius.circular(12.w)),
|
||
image: DecorationImage(
|
||
image: AssetImage('ep_sel_bg.png'.ktIcon),
|
||
fit: BoxFit.fill,
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Container(
|
||
margin: EdgeInsets.only(top: 80.w),
|
||
child: Row(
|
||
children: [
|
||
Image.asset('ip.png'.ktIcon, width: 38.w),
|
||
SizedBox(width: 6.w),
|
||
Container(
|
||
padding: EdgeInsets.symmetric(
|
||
vertical: 9.w,
|
||
horizontal: 14.w,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: Color(0xFF1E1E20),
|
||
borderRadius: BorderRadius.circular(100),
|
||
),
|
||
child: Row(
|
||
children: [
|
||
Text(
|
||
'Select Episode',
|
||
style: TextStyle(
|
||
fontSize: 15.sp,
|
||
fontWeight: FontWeight.w500,
|
||
color: Color(0xFFA7F62F),
|
||
),
|
||
),
|
||
Text(
|
||
'(All ${state.video?.shortPlayInfo?.episodeTotal} episodes)',
|
||
style: TextStyle(
|
||
fontSize: 12.sp,
|
||
color: Color(0xFFA7F62F),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
Container(
|
||
margin: EdgeInsets.only(top: 16.w),
|
||
child: Row(
|
||
children: [
|
||
KtNetworkImage(
|
||
imageUrl: state.video?.shortPlayInfo?.imageUrl ?? '',
|
||
width: 64.w,
|
||
height: 85.w,
|
||
borderRadius: BorderRadius.circular(6.w),
|
||
),
|
||
SizedBox(width: 10.w),
|
||
SizedBox(
|
||
width: ScreenUtil().screenWidth - 105.w,
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
Text(
|
||
state.video?.shortPlayInfo?.name ?? '',
|
||
maxLines: 1,
|
||
overflow: TextOverflow.ellipsis,
|
||
style: TextStyle(
|
||
fontSize: 14.sp,
|
||
color: Color(0xFF1E1E20),
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
SizedBox(height: 7.w),
|
||
Text(
|
||
state.video?.shortPlayInfo?.category?.first ?? '',
|
||
maxLines: 1,
|
||
overflow: TextOverflow.ellipsis,
|
||
style: TextStyle(
|
||
fontSize: 12.sp,
|
||
color: Color(0xFFA7F62F),
|
||
),
|
||
),
|
||
SizedBox(height: 9.w),
|
||
if (!KtUtils.isEmpty(
|
||
state.video?.shortPlayInfo?.description,
|
||
))
|
||
SizedBox(
|
||
height: 40.w,
|
||
child: Text(
|
||
state.video?.shortPlayInfo?.description ?? '',
|
||
maxLines: 2,
|
||
overflow: TextOverflow.ellipsis,
|
||
style: TextStyle(
|
||
fontSize: 10.sp,
|
||
color: Color(0xFF5E5E5E),
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
SizedBox(height: 19.w),
|
||
Expanded(
|
||
child: GridView.builder(
|
||
padding: EdgeInsets.only(bottom: 16.w),
|
||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||
crossAxisCount: 5,
|
||
mainAxisSpacing: 10.w,
|
||
crossAxisSpacing: 10.w,
|
||
childAspectRatio: 61 / 50,
|
||
),
|
||
itemCount: state.episodeList.length,
|
||
itemBuilder: (context, index) {
|
||
bool isCurrent = index == logic.currentIndex;
|
||
|
||
return GestureDetector(
|
||
onTap: () {
|
||
Get.back();
|
||
bool? beforeEpUnlock = index - 1 <= 0
|
||
? false
|
||
: state.episodeList[index - 1].isLock;
|
||
if (beforeEpUnlock ?? false) {
|
||
KtToastUtils.showError('Unable to unlock episodes');
|
||
return;
|
||
}
|
||
logic.onPageChanged(index, isToggle: true);
|
||
},
|
||
child: Stack(
|
||
children: [
|
||
Container(
|
||
decoration: BoxDecoration(
|
||
color: isCurrent ? Color(0xFFA7F62F) : Colors.white,
|
||
borderRadius: BorderRadius.circular(10.w),
|
||
border: isCurrent
|
||
? Border.all(
|
||
color: Color(0xFF1E1E20),
|
||
width: 1.w,
|
||
)
|
||
: null,
|
||
),
|
||
child: Center(
|
||
child: Text(
|
||
'${index + 1}',
|
||
style: TextStyle(
|
||
color: Color(0xFF1E1E20),
|
||
fontSize: 14.sp,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
if (state.episodeList[index].isLock == true)
|
||
Positioned(
|
||
right: 5.w,
|
||
top: 5.w,
|
||
child: Image.asset(
|
||
'ic_lock.png'.ktIcon,
|
||
width: 12.w,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
),
|
||
),
|
||
],
|
||
),
|
||
),
|
||
isScrollControlled: true,
|
||
);
|
||
}
|
||
|
||
showSpeedSelDialog() {
|
||
return showModalBottomSheet(
|
||
context: context,
|
||
isScrollControlled: true,
|
||
builder: (context) => StatefulBuilder(
|
||
builder: (context, dialogState) {
|
||
return Container(
|
||
height: 375.w,
|
||
padding: EdgeInsets.symmetric(horizontal: 15.w),
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.vertical(top: Radius.circular(12.w)),
|
||
image: DecorationImage(
|
||
image: AssetImage('ic_speed_bg.png'.ktIcon),
|
||
fit: BoxFit.fill,
|
||
),
|
||
),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
SizedBox(height: 44.w),
|
||
Row(
|
||
children: [
|
||
Container(
|
||
margin: EdgeInsets.only(right: 5.w),
|
||
child: Image.asset(
|
||
'ic_cur_speed.png'.ktIcon,
|
||
width: 24.w,
|
||
),
|
||
),
|
||
Text(
|
||
'Playback Speed',
|
||
style: TextStyle(
|
||
fontSize: 18.sp,
|
||
color: Colors.white,
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
SizedBox(width: 12.w),
|
||
Text(
|
||
'· ${state.currentSpeed}X',
|
||
style: TextStyle(
|
||
fontSize: 16.sp,
|
||
color: Color(0xFF36F2FF),
|
||
fontWeight: FontWeight.w500,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
SizedBox(height: 15.w),
|
||
|
||
Expanded(
|
||
child: ListView.separated(
|
||
shrinkWrap: true,
|
||
itemBuilder: (context, index) => GestureDetector(
|
||
onTap: () {
|
||
_selectSpeed(state.speedList[index]);
|
||
Get.back();
|
||
},
|
||
child: Container(
|
||
padding: EdgeInsets.all(12.w),
|
||
alignment: Alignment.centerLeft,
|
||
decoration: BoxDecoration(
|
||
borderRadius: BorderRadius.circular(8.w),
|
||
color: Colors.white.withValues(alpha: .16),
|
||
border: state.currentSpeed == state.speedList[index]
|
||
? Border.all(width: 2.w, color: Color(0xFF36F2FF))
|
||
: null,
|
||
),
|
||
child: Text(
|
||
'${state.speedList[index]}x',
|
||
style: TextStyle(
|
||
fontSize: 14.sp,
|
||
color: Colors.white,
|
||
),
|
||
),
|
||
),
|
||
),
|
||
separatorBuilder: (_, __) => SizedBox(height: 10.w),
|
||
itemCount: state.speedList.length,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
}
|