791 lines
29 KiB
Dart
791 lines
29 KiB
Dart
import 'dart:io';
|
||
import 'dart:ui';
|
||
|
||
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_model/kt_video_detail_bean.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 (controller != null && controller.value.isInitialized && !isAllOver)
|
||
GestureDetector(
|
||
onTap: () {
|
||
// if (episode.isLock == true) return;
|
||
controller.value.isPlaying ? controller.pause() : controller.play();
|
||
setState(() {});
|
||
},
|
||
child: Container(
|
||
width: ScreenUtil().screenWidth,
|
||
height: ScreenUtil().screenHeight,
|
||
color: Colors.transparent,
|
||
),
|
||
),
|
||
// if (episode.isLock == true)
|
||
// Container(
|
||
// width: ScreenUtil().screenWidth,
|
||
// height: ScreenUtil().screenHeight,
|
||
// color: Colors.black.withValues(alpha: .75),
|
||
// child: Center(
|
||
// child: GestureDetector(
|
||
// onTap: () {
|
||
// EasyThrottle.throttle(
|
||
// 'unlock',
|
||
// Duration(seconds: 2),
|
||
// () => logic.buyVideo(
|
||
// episode.id!,
|
||
// episode.coins ?? 0,
|
||
// toRecharge: true,
|
||
// ),
|
||
// );
|
||
// },
|
||
// child: Container(
|
||
// width: 260.w,
|
||
// padding: EdgeInsets.symmetric(vertical: 17.w),
|
||
// decoration: MyStyles.mainBorder(width: 1, radius: 50),
|
||
// child: Row(
|
||
// mainAxisAlignment: MainAxisAlignment.center,
|
||
// children: [
|
||
// Image.asset('ic_unlock_lock.png'.ktIcon, width: 20.w),
|
||
// SizedBox(width: 3.w),
|
||
// Text(
|
||
// state.curUnlock == index
|
||
// ? 'Unlocking costs ${episode.coins ?? 0} coins'
|
||
// : 'Prev.locked',
|
||
// style: TextStyle(
|
||
// fontSize: 14.sp,
|
||
// color: DrColors.mainWhite,
|
||
// ),
|
||
// ),
|
||
// ],
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// ),
|
||
// 顶部返回
|
||
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: Platform.isAndroid
|
||
? SafeArea(child: bottomView(index))
|
||
: bottomView(index),
|
||
),
|
||
],
|
||
);
|
||
}
|
||
|
||
Widget bottomView(int index) {
|
||
return Container(
|
||
width: ScreenUtil().screenWidth,
|
||
padding: EdgeInsets.fromLTRB(15.w, 40.w, 15.w, 50.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 - 2 <= 0 ? false : state.episodeList[index - 2].isLock;
|
||
// if (beforeEpUnlock ?? false) {
|
||
// ToastUtils.show('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[episode - 1].isLock == true)
|
||
// Positioned(
|
||
// right: 0,
|
||
// top: 0,
|
||
// child: Container(
|
||
// width: 20.w,
|
||
// height: 12.w,
|
||
// decoration: BoxDecoration(
|
||
// color: ColorResource.mainYellow,
|
||
// borderRadius: BorderRadius.only(
|
||
// topRight: Radius.circular(6.w),
|
||
// bottomLeft: Radius.circular(6.w),
|
||
// ),
|
||
// ),
|
||
// child: Image.asset(
|
||
// 'ic_video_lock.png'.icon,
|
||
// width: 10.w,
|
||
// height: 10.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,
|
||
),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
},
|
||
),
|
||
);
|
||
}
|
||
}
|