Compare commits

..

7 Commits
main ... dev3

Author SHA1 Message Date
yyy
7527e94565 feat: 更改适配版本号 2025-10-17 11:27:28 +08:00
pengtao
29708e23af feat:金币包签到页面 2025-10-16 16:34:07 +08:00
pengtao
191c7fa58d feat:首页金币包弹框 2025-10-15 15:53:36 +08:00
pengtao
1cca41b1de feat:View All 点击区域过小,和下拉刷新事件冲突 2025-10-15 13:57:32 +08:00
pengtao
74976ae83a feat:进页面seekTo 2025-10-14 17:53:29 +08:00
pengtao
bcdce80889 feat:详情推荐,首页悬浮小视频卡片,5s自动隐藏 2025-10-14 16:02:11 +08:00
pengtao
be0d7eae45 feat:我的页面,首次重复加载修改 2025-10-14 15:55:11 +08:00
39 changed files with 2428 additions and 383 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
assets/home_coins_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

BIN
assets/home_coins_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
assets/home_coins_close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
assets/home_coins_right.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
assets/refills_top_bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 585 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

BIN
assets/refills_top_btn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

View File

@ -51,7 +51,11 @@ class KtApis {
static const String createGooglePay = "/googlePaid";
static const String buyVideo = "/buy_video";
static const String openNotify = "/openNotify";
static const String getReceiveDayCoin = "/getReceiveDayCoin";
static const String receiveDayCoin = "/receiveDayCoin";
static const String getReceiveDayCoinInfo = "/getReceiveDayCoinInfo";
static const String getRetainVipPaySetting = "/getRetainVipPaySetting";
///
static const String getCustomerOrder = "/getCustomerOrder";
static const String getCustomerBuyRecords = "/getCustomerBuyRecords";

View File

@ -0,0 +1,176 @@
import 'dart:convert';
/// week_max_total : 10000
/// week_total : 1600
/// receive_coins : 500
/// receive_count : 2
/// receive_list : [{"id":1,"title":"test","week_max_total":2400,"week_total":600,"receive_coins":200,"day_text":"4/13"},{"id":5,"title":"Weekly Subscribe Coin Pack","week_max_total":7600,"week_total":1000,"receive_coins":300,"day_text":"3/23"}]
ReceiveCoinInfo ktReceiveCoinInfoFromJson(String str) => ReceiveCoinInfo.fromJson(json.decode(str));
String ktReceiveCoinInfoToJson(ReceiveCoinInfo data) => json.encode(data.toJson());
class ReceiveCoinInfo {
ReceiveCoinInfo({
int? weekMaxTotal,
int? weekTotal,
int? receiveCoins,
int? receiveCount,
List<ReceiveList>? receiveList,
}) {
_weekMaxTotal = weekMaxTotal;
_weekTotal = weekTotal;
_receiveCoins = receiveCoins;
_receiveCount = receiveCount;
_receiveList = receiveList;
}
ReceiveCoinInfo.fromJson(dynamic json) {
_weekMaxTotal = json['week_max_total'];
_weekTotal = json['week_total'];
_receiveCoins = json['receive_coins'];
_receiveCount = json['receive_count'];
if (json['receive_list'] != null) {
_receiveList = [];
json['receive_list'].forEach((v) {
_receiveList?.add(ReceiveList.fromJson(v));
});
}
}
int? _weekMaxTotal;
int? _weekTotal;
int? _receiveCoins;
int? _receiveCount;
List<ReceiveList>? _receiveList;
ReceiveCoinInfo copyWith({
int? weekMaxTotal,
int? weekTotal,
int? receiveCoins,
int? receiveCount,
List<ReceiveList>? receiveList,
}) => ReceiveCoinInfo(
weekMaxTotal: weekMaxTotal ?? _weekMaxTotal,
weekTotal: weekTotal ?? _weekTotal,
receiveCoins: receiveCoins ?? _receiveCoins,
receiveCount: receiveCount ?? _receiveCount,
receiveList: receiveList ?? _receiveList,
);
int? get weekMaxTotal => _weekMaxTotal;
int? get weekTotal => _weekTotal;
int? get receiveCoins => _receiveCoins;
int? get receiveCount => _receiveCount;
List<ReceiveList>? get receiveList => _receiveList;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['week_max_total'] = _weekMaxTotal;
map['week_total'] = _weekTotal;
map['receive_coins'] = _receiveCoins;
map['receive_count'] = _receiveCount;
if (_receiveList != null) {
map['receive_list'] = _receiveList?.map((v) => v.toJson()).toList();
}
return map;
}
}
/// id : 1
/// title : "test"
/// week_max_total : 2400
/// week_total : 600
/// receive_coins : 200
/// week_remaining_total : 200
/// day_text : "4/13"
ReceiveList receiveListFromJson(String str) => ReceiveList.fromJson(json.decode(str));
String receiveListToJson(ReceiveList data) => json.encode(data.toJson());
class ReceiveList {
ReceiveList({
int? id,
String? title,
int? weekMaxTotal,
int? weekTotal,
int? receiveCoins,
int? weekRemainingTotal,
String? dayText,
}) {
_id = id;
_title = title;
_weekMaxTotal = weekMaxTotal;
_weekTotal = weekTotal;
_receiveCoins = receiveCoins;
_weekRemainingTotal = weekRemainingTotal;
_dayText = dayText;
}
ReceiveList.fromJson(dynamic json) {
_id = json['id'];
_title = json['title'];
_weekMaxTotal = json['week_max_total'];
_weekTotal = json['week_total'];
_receiveCoins = json['receive_coins'];
_weekRemainingTotal = json['week_remaining_total'];
_dayText = json['day_text'];
}
int? _id;
String? _title;
int? _weekMaxTotal;
int? _weekTotal;
int? _receiveCoins;
int? _weekRemainingTotal;
String? _dayText;
ReceiveList copyWith({
int? id,
String? title,
int? weekMaxTotal,
int? weekTotal,
int? receiveCoins,
int? weekRemainingTotal,
String? dayText,
}) => ReceiveList(
id: id ?? _id,
title: title ?? _title,
weekMaxTotal: weekMaxTotal ?? _weekMaxTotal,
weekTotal: weekTotal ?? _weekTotal,
receiveCoins: receiveCoins ?? _receiveCoins,
weekRemainingTotal: weekRemainingTotal ?? _weekRemainingTotal,
dayText: dayText ?? _dayText,
);
int? get id => _id;
String? get title => _title;
int? get weekMaxTotal => _weekMaxTotal;
int? get weekTotal => _weekTotal;
int? get receiveCoins => _receiveCoins;
int? get weekRemainingTotal => _weekRemainingTotal;
String? get dayText => _dayText;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = _id;
map['title'] = _title;
map['week_max_total'] = _weekMaxTotal;
map['week_total'] = _weekTotal;
map['receive_coins'] = _receiveCoins;
map['week_remaining_total'] = _weekRemainingTotal;
map['day_text'] = _dayText;
return map;
}
}

View File

@ -359,6 +359,7 @@ class VideoInfo {
num? isVip,
Revolution? revolution,
String? imageUrl,
String? playSeconds,
}) {
_id = id;
_shortPlayVideoId = shortPlayVideoId;
@ -372,6 +373,7 @@ class VideoInfo {
_isVip = isVip;
_revolution = revolution;
_imageUrl = imageUrl;
_playSeconds = playSeconds;
}
VideoInfo.fromJson(dynamic json) {
@ -389,6 +391,7 @@ class VideoInfo {
? Revolution.fromJson(json['revolution'])
: null;
_imageUrl = json['image_url'];
_playSeconds = json['play_seconds'];
}
num? _id;
@ -403,7 +406,7 @@ class VideoInfo {
num? _isVip;
Revolution? _revolution;
String? _imageUrl;
String? _playSeconds;
VideoInfo copyWith({
num? id,
num? shortPlayVideoId,
@ -417,6 +420,7 @@ class VideoInfo {
num? isVip,
Revolution? revolution,
String? imageUrl,
String? playSeconds,
}) => VideoInfo(
id: id ?? _id,
shortPlayVideoId: shortPlayVideoId ?? _shortPlayVideoId,
@ -430,6 +434,7 @@ class VideoInfo {
isVip: isVip ?? _isVip,
revolution: revolution ?? _revolution,
imageUrl: imageUrl ?? _imageUrl,
playSeconds: playSeconds ?? _playSeconds,
);
num? get id => _id;
@ -456,6 +461,8 @@ class VideoInfo {
String? get imageUrl => _imageUrl;
String? get playSeconds => _playSeconds;
set id(num? value) => _id = value;
set shortPlayVideoId(num? value) => _shortPlayVideoId = value;
@ -480,6 +487,8 @@ class VideoInfo {
set imageUrl(String? value) => _imageUrl = value;
set playSeconds(String? value) => _playSeconds = value;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['id'] = _id;
@ -496,6 +505,7 @@ class VideoInfo {
map['revolution'] = _revolution?.toJson();
}
map['image_url'] = _imageUrl;
map['play_seconds'] = _playSeconds;
return map;
}
}

View File

@ -1,5 +1,7 @@
import 'package:easy_refresh/easy_refresh.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_kinetra/kt_utils/kt_string_extend.dart';
import 'package:flutter_kinetra/kt_model/kt_home_category_bean.dart';
import 'package:flutter_kinetra/kt_pages/kt_home/state.dart';
import 'package:get/get.dart';
@ -8,6 +10,7 @@ import '../../dio_cilent/kt_apis.dart';
import '../../dio_cilent/kt_request.dart';
import '../../kt_model/kt_short_video_bean.dart';
import '../../kt_widgets/kt_status_widget.dart';
import '../kt_routes.dart';
class KtHomeLogic extends GetxController {
final state = KtHomeState();
@ -21,6 +24,7 @@ class KtHomeLogic extends GetxController {
void onReady() {
super.onReady();
refreshData();
getReceiveDayCoin();
}
refreshData() {
@ -178,4 +182,101 @@ class KtHomeLogic extends GetxController {
update();
}
}
getReceiveDayCoin() async {
ApiResponse res = await KtHttpClient().request(
KtApis.getReceiveDayCoin,
method: HttpMethod.get,
);
if (res.success) {
state.receiveCoin = res.data['coins'];
state.hasSubCoin = res.data['is_exist_sub'] == 1 && state.receiveCoin > 0;
if (state.hasSubCoin) {
Get.dialog(
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 301.w,
height: 260.w,
padding: EdgeInsets.only(top: 140.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('home_coins_bg.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Column(
children: [
Text(
'Daily Reward',
style: TextStyle(
color: Color(0xFFFF4306),
fontSize: 22.sp,
fontWeight: FontWeight.w900,
),
),
Text(
'is waiting!',
style: TextStyle(
color: Colors.black,
fontSize: 20.sp,
fontWeight: FontWeight.w700,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('ic_coin.png'.ktIcon, width: 28.w),
SizedBox(width: 4.w),
Text(
'+${state.receiveCoin}',
style: TextStyle(
color: Color(0xFFFF9500),
fontSize: 22.sp,
fontWeight: FontWeight.w700,
),
),
],
),
],
),
),
GestureDetector(
onTap: () {
Get.back();
Get.toNamed(KtRoutes.refill);
},
child: Container(
width: 200.w,
height: 50.w,
margin: EdgeInsets.only(bottom: 12.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('home_coins_btn.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Center(
child: Text(
'Claim Now',
style: TextStyle(
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.w700,
),
),
),
),
),
GestureDetector(
onTap: () => Get.back(),
child: Image.asset('home_coins_close.png'.ktIcon, width: 24.w),
),
],
),
);
}
}
}
}

View File

@ -66,6 +66,125 @@ class _KtHomePageState extends State<KtHomePage>
),
),
),
Positioned(
bottom: 90.w,
right: 0,
child: GestureDetector(
onTap: () => Get.toNamed(KtRoutes.refill),
child: Stack(
children: [
Image.asset(
'home_coins_right.png'.ktIcon,
width: 100.w,
height: 69.w,
),
Container(
width: 84.w,
height: 22.w,
margin: EdgeInsets.only(top: 54.w, left: 8.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('home_coins_right_btn.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Center(
child: Text(
'Daily Coins',
style: TextStyle(
color: Colors.white,
fontSize: 12.sp,
fontWeight: FontWeight.w800,
),
),
),
),
],
),
),
),
//
if (state.showVideo && state.curVideo != null)
Positioned(
bottom: 2.w,
right: 15.w,
child: GestureDetector(
onTap: () => Get.toNamed(
KtRoutes.shortVideo,
arguments: {
'shortPlayId': state.curVideo?.shortPlayId,
'imageUrl': state.curVideo?.imageUrl ?? '',
},
),
child: Container(
width: 345.w,
height: 40.w,
padding: EdgeInsets.only(right: 10.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.w),
),
child: Row(
children: [
Container(
width: 40.w,
height: 40.w,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('home_bottom_video.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Center(
child: KtNetworkImage(
imageUrl: state.curVideo?.imageUrl ?? '',
width: 30.w,
height: 30.w,
borderRadius: BorderRadius.circular(15.w),
),
),
),
SizedBox(width: 8.w),
Container(
constraints: BoxConstraints(maxWidth: 160.w),
child: Text(
state.curVideo?.name ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14.sp,
fontWeight: FontWeight.w400,
color: Color(0xFF1E1E20),
),
),
),
SizedBox(width: 20.w),
Text(
'Ep.${state.curVideo?.process}',
style: TextStyle(
fontSize: 12.sp,
fontWeight: FontWeight.w400,
color: Color(0xFF94949B),
),
),
const Spacer(),
Image.asset(
'ic_home_play.png'.ktIcon,
width: 24.w,
height: 24.w,
),
SizedBox(width: 14.w),
Image.asset(
'ic_home_eposide.png'.ktIcon,
width: 24.w,
height: 24.w,
),
],
),
),
),
),
],
);
},
@ -1098,8 +1217,7 @@ class _KtHomePageState extends State<KtHomePage>
height: 127.w,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
video.name ?? '',

View File

@ -95,6 +95,7 @@ class _KtMainPageState extends State<KtMainPage>
itemCount: _tabsTitle.length,
),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
selectedItemColor: Color(0xFF1E1E20),
selectedLabelStyle: TextStyle(
fontSize: 10.sp,
@ -102,7 +103,7 @@ class _KtMainPageState extends State<KtMainPage>
),
unselectedLabelStyle: TextStyle(
fontSize: 10.sp,
color: Color(0xFF95959C),
color: Color(0xFF94949B),
fontWeight: FontWeight.w400,
),
type: BottomNavigationBarType.fixed,

View File

@ -1,12 +1,13 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_kinetra/kt_pages/kt_mine/kt_store/state.dart';
import 'package:get/get.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:get/get.dart';
import '../../../dio_cilent/kt_apis.dart';
import '../../../dio_cilent/kt_request.dart';
@ -18,7 +19,10 @@ import '../../../kt_utils/kt_iap_util.dart';
import '../../../kt_utils/kt_purchase_restore_utils.dart';
import '../../../kt_utils/kt_toast_utils.dart';
import '../../../kt_utils/kt_utils.dart';
import '../../../kt_utils/kt_string_extend.dart';
// import '../../../kt_utils/logger_service.dart';
import '../logic.dart';
import 'state.dart';
class KtStoreLogic extends GetxController {
final state = KtStoreState();
@ -68,8 +72,9 @@ class KtStoreLogic extends GetxController {
refreshController.refreshCompleted();
if (res.success) {
state.storeBean = KtStoreBean.fromJson(res.data);
// appLogger.d(state.storeBean);
update();
// initStore();
initStore();
}
} catch (e) {
EasyLoading.dismiss();
@ -305,12 +310,266 @@ class KtStoreLogic extends GetxController {
buyGoods(KtGoodsBean payItem, {num? shortPlayId, num? videoId}) {
if (payItem.buyType == 'sub_coins') {
// showSubCoinCheckDialog(payItem, shortPlayId: shortPlayId, videoId: videoId);
showSubCoinCheckDialog(
payItem,
shortPlayId: shortPlayId,
videoId: videoId,
);
} else {
createOrder(payItem, shortPlayId: shortPlayId, videoId: videoId);
}
}
//
showSubCoinCheckDialog(
KtGoodsBean payItem, {
num? shortPlayId,
num? videoId,
}) {
Get.bottomSheet(
isScrollControlled: true,
backgroundColor: Colors.transparent,
Container(
width: ScreenUtil().screenWidth,
height: 630.w,
padding: EdgeInsets.fromLTRB(15.w, 80.w, 15.w, 0),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('refills_bottomSheet_bg.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
GestureDetector(
onTap: () => Get.back(),
child: Image.asset(
'home_coins_close.png'.ktIcon,
width: 24.w,
),
),
],
),
Column(
children: [
Text(
'What You Get',
style: TextStyle(
color: const Color(0xFF79C900),
fontSize: 24.sp,
fontFamily: 'Inter',
fontWeight: FontWeight.w800,
height: 1,
),
),
SizedBox(height: 24.w),
Container(
width: 345.w,
height: 147.w,
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14.w),
),
child: Column(
children: [
Text(
'Bonus You Get',
style: TextStyle(
color: const Color(0xFF1E1E20) /* 黑 */,
fontSize: 18.sp,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.33,
),
),
SizedBox(height: 8.w),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 150.w,
height: 83.w,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFFF5FFD1), Color(0xFFCFFF99)],
),
borderRadius: BorderRadius.circular(8.w),
),
child: Column(
children: [
SizedBox(height: 7.w),
Image.asset(
'refills_bottomSheet_l.png'.ktIcon,
width: 44.w,
),
SizedBox(height: 10.w),
Text(
'Weekly Refill Package',
style: TextStyle(
color: const Color(0xFF1E1E20),
fontSize: 12.sp,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.33,
),
),
],
),
),
Container(
width: 150.w,
height: 83.w,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xFFF5FFD1), Color(0xFFCFFF99)],
),
borderRadius: BorderRadius.circular(8.w),
),
child: Column(
children: [
SizedBox(height: 7.w),
Image.asset(
'refills_bottomSheet_r.png'.ktIcon,
width: 44.w,
),
SizedBox(height: 10.w),
Text(
'Daily Bonuses',
style: TextStyle(
color: const Color(0xFF1E1E20),
fontSize: 12.sp,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.33,
),
),
],
),
),
],
),
],
),
),
SizedBox(height: 14.w),
Container(
width: 345.w,
padding: EdgeInsets.fromLTRB(16.w, 16.w, 16.w, 0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14.w),
),
child: Column(
children: [
Text(
'How Do I Receive Coins?',
style: TextStyle(
color: const Color(0xFF1E1E20),
fontSize: 18.sp,
fontFamily: 'Inter',
fontWeight: FontWeight.w700,
height: 1.33,
),
),
SizedBox(height: 32.w),
ListView.separated(
shrinkWrap: true,
padding: EdgeInsets.zero,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => Row(
children: [
Image.asset(
'ic_coin.png'.ktIcon,
width: 16.w,
height: 16.w,
),
SizedBox(width: 8.w),
Expanded(
child: Text(
payItem.extInfo?.subCoinsTxtList?[index] ?? '',
style: TextStyle(
color: const Color(0xFF1E1E20),
fontSize: 14.sp,
fontFamily: 'Inter',
fontWeight: FontWeight.w400,
height: 1.29,
),
),
),
],
),
separatorBuilder: (_, __) => SizedBox(height: 16.w),
itemCount:
payItem.extInfo?.subCoinsTxtList?.length ?? 0,
),
],
),
),
SizedBox(height: 14.w),
Text(
'${payItem.productDetails?.price ?? ''}/week',
style: TextStyle(
color: const Color(0xFFFF9500),
fontSize: 18.sp,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 9.w),
GestureDetector(
onTap: () {
Get.back();
createOrder(
payItem,
shortPlayId: shortPlayId,
videoId: videoId,
);
},
child: Stack(
children: [
Image.asset(
'refills_bottomSheet_btn.png'.ktIcon,
width: 345.w,
height: 68.w,
),
Container(
height: 46.w,
margin: EdgeInsets.only(top: 5.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Continue',
style: TextStyle(
color: Color(0xFF1E1E20),
fontSize: 14.sp,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
height: 1.1,
),
),
],
),
),
],
),
),
],
),
],
),
),
);
}
createOrder(KtGoodsBean goods, {num? shortPlayId, num? videoId}) async {
EasyLoading.show(status: 'Paying...', maskType: EasyLoadingMaskType.black);
// Map<String, dynamic> params = {"pay_setting_id": goods.id!};

View File

@ -38,8 +38,8 @@ class MyListLogic extends GetxController {
state.loadStatus = KtLoadStatusType.loading;
if (refresh) {
state.curFavoriteIndex = 1;
state.favoriteList.clear();
state.chestList.clear();
// state.favoriteList.clear();
// state.chestList.clear();
}
if (loadMore) {
state.curFavoriteIndex++;
@ -64,6 +64,10 @@ class MyListLogic extends GetxController {
refreshCtrl.loadNoData();
chestRefreshCtrl.loadNoData();
}
if (refresh) {
state.favoriteList.clear();
state.chestList.clear();
}
state.favoriteList.addAll(list);
if (state.favoriteList.length > 3) {
state.chestList = state.favoriteList.sublist(0, 3);
@ -86,7 +90,7 @@ class MyListLogic extends GetxController {
getHistoryList({bool refresh = false, bool loadMore = false}) async {
if (refresh) {
state.curHistoryIndex = 1;
state.historyList.clear();
// state.historyList.clear();
}
if (loadMore) {
state.curHistoryIndex++;
@ -107,7 +111,9 @@ class MyListLogic extends GetxController {
...res.data['list'].map((item) => KtHistoryVideoBean.fromJson(item)),
];
if (list.length < 20) refreshCtrl.loadNoData();
if (refresh) {
state.historyList.clear();
}
state.historyList.addAll(list);
update();
} else {

View File

@ -39,286 +39,288 @@ class _KtMyListPageState extends State<KtMyListPage> {
),
child: state.chestList.isEmpty && state.historyList.isEmpty
? KtStatusWidget(
type: KtErrorStatusType.nothingYet,
onPressed: logic.initData,
)
type: KtErrorStatusType.nothingYet,
onPressed: logic.initData,
)
: SmartRefresher(
controller: logic.refreshCtrl,
enablePullUp: true,
enablePullDown: true,
onRefresh: () => logic.initData(),
onLoading: () => logic.getHistoryList(loadMore: true),
child: Column(
children: [
if (state.chestList.isNotEmpty)
Container(
width: ScreenUtil().screenWidth - 30.w,
padding: EdgeInsets.fromLTRB(6.w, 53.w, 6.w, 6.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('collect_bg.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Column(
children: [
Row(
children: [
SizedBox(width: 6.w),
Text(
'My Treasure Chest',
style: TextStyle(
fontSize: 18.sp,
color: Colors.white,
fontWeight: FontWeight.w800,
fontStyle: FontStyle.italic,
),
controller: logic.refreshCtrl,
enablePullUp: true,
enablePullDown: true,
onRefresh: () => logic.initData(),
onLoading: () => logic.getHistoryList(loadMore: true),
child: Column(
children: [
if (state.chestList.isNotEmpty)
Container(
width: ScreenUtil().screenWidth - 30.w,
padding: EdgeInsets.fromLTRB(6.w, 46.w, 6.w, 6.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('collect_bg.png'.ktIcon),
fit: BoxFit.fill,
),
const Spacer(),
GestureDetector(
onTap: () => Get.to(KtMyChestPage()),
child: Row(
),
child: Column(
children: [
Row(
children: [
SizedBox(width: 6.w),
Text(
'View All',
'My Treasure Chest',
style: TextStyle(
fontSize: 12.sp,
color: Color(0xFFD6D6D6),
fontSize: 18.sp,
color: Colors.white,
fontWeight: FontWeight.w800,
fontStyle: FontStyle.italic,
),
),
Image.asset(
'ic_right_white.png'.ktIcon,
width: 10.w,
const Spacer(),
GestureDetector(
onTap: () => Get.to(KtMyChestPage()),
child: Container(
padding: EdgeInsets.all(8.w),
child: Row(
children: [
Text(
'View All',
style: TextStyle(
fontSize: 12.sp,
color: Color(0xFFD6D6D6),
),
),
Image.asset(
'ic_right_white.png'.ktIcon,
width: 10.w,
),
],
),
),
),
SizedBox(width: 6.w),
],
),
),
],
),
SizedBox(height: 15.w),
Container(
height: 145.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14.w),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
...state.chestList.map(
(video) => Container(
margin: EdgeInsets.symmetric(
horizontal: 5.w,
),
child: Stack(
children: [
GestureDetector(
onTap: () => Get.toNamed(
KtRoutes.shortVideo,
arguments: {
'shortPlayId':
video.shortPlayId,
'imageUrl':
video.imageUrl ?? '',
},
SizedBox(height: 8.w),
Container(
height: 145.w,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14.w),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
...state.chestList.map(
(video) => Container(
margin: EdgeInsets.symmetric(
horizontal: 5.w,
),
child: KtNetworkImage(
imageUrl: video.imageUrl ?? '',
width: 100.w,
height: 133.w,
borderRadius:
BorderRadius.circular(6.w),
),
),
Positioned(
right: 4.w,
top: 4.w,
child: GestureDetector(
onTap: () =>
logic.cancelCollect(
video.shortPlayId!,
child: Stack(
children: [
GestureDetector(
onTap: () => Get.toNamed(
KtRoutes.shortVideo,
arguments: {
'shortPlayId':
video.shortPlayId,
'imageUrl':
video.imageUrl ?? '',
},
),
child: Image.asset(
'ic_collect_sel.png'.ktIcon,
width: 28.w,
),
child: KtNetworkImage(
imageUrl: video.imageUrl ?? '',
width: 100.w,
height: 133.w,
borderRadius:
BorderRadius.circular(6.w),
),
),
Positioned(
right: 4.w,
top: 4.w,
child: GestureDetector(
onTap: () =>
logic.cancelCollect(
video.shortPlayId!,
),
child: Image.asset(
'ic_collect_sel.png'.ktIcon,
width: 28.w,
),
),
),
],
),
),
],
),
),
],
),
),
],
),
),
],
),
),
SizedBox(height: 15.w),
Expanded(
child: Container(
width: ScreenUtil().screenWidth,
padding: EdgeInsets.all(15.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(20.w),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Continue Watching',
style: TextStyle(
fontSize: 18.sp,
color: Color(0xFF1E1E20),
fontWeight: FontWeight.w800,
fontStyle: FontStyle.italic,
SizedBox(height: 15.w),
Expanded(
child: Container(
width: ScreenUtil().screenWidth,
padding: EdgeInsets.all(15.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.vertical(
top: Radius.circular(20.w),
),
),
),
Expanded(
child: ListView.separated(
padding: EdgeInsets.only(top: 10.w),
// shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
KtHistoryVideoBean video =
state.historyList[index];
return GestureDetector(
onTap: () => Get.toNamed(
KtRoutes.shortVideo,
arguments: {
'shortPlayId': video.shortPlayId,
'imageUrl': video.imageUrl ?? '',
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Continue Watching',
style: TextStyle(
fontSize: 18.sp,
color: Color(0xFF1E1E20),
fontWeight: FontWeight.w800,
fontStyle: FontStyle.italic,
),
child: Container(
color: Colors.transparent,
child: Row(
children: [
KtNetworkImage(
imageUrl: video.imageUrl ?? '',
width: 74.w,
height: 98.w,
borderRadius:
BorderRadius.circular(8.w),
),
Expanded(
child: ListView.separated(
padding: EdgeInsets.only(top: 10.w),
// shrinkWrap: true,
// physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
KtHistoryVideoBean video =
state.historyList[index];
return GestureDetector(
onTap: () => Get.toNamed(
KtRoutes.shortVideo,
arguments: {
'shortPlayId': video.shortPlayId,
'imageUrl': video.imageUrl ?? '',
},
),
SizedBox(width: 13.w),
SizedBox(
width: 200.w,
height: 98.w,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
child: Container(
color: Colors.transparent,
child: Row(
children: [
Text(
video.name ?? '',
maxLines: 1,
overflow:
TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13.sp,
color: Color(0xFF1E1E20),
fontWeight:
FontWeight.w500,
),
KtNetworkImage(
imageUrl: video.imageUrl ?? '',
width: 74.w,
height: 98.w,
borderRadius:
BorderRadius.circular(8.w),
),
SizedBox(height: 6.w),
Text(
video.category?.first ?? '',
maxLines: 1,
overflow:
TextOverflow.ellipsis,
style: TextStyle(
fontSize: 10.sp,
color: Color(0xFF79C900),
fontWeight:
FontWeight.w400,
),
),
SizedBox(height: 20.w),
Row(
children: [
SizedBox(
width: 26.w,
height: 26.w,
child: CircularProgressIndicator(
value:
(video.currentEpisode ??
0) /
(video.episodeTotal! >
0
? video
.episodeTotal!
: 1),
backgroundColor:
Color(0xFFD9D9D9),
valueColor:
AlwaysStoppedAnimation<
Color
>(
Color(
0xFFA7F62F,
SizedBox(width: 13.w),
SizedBox(
width: 200.w,
height: 98.w,
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisAlignment:
MainAxisAlignment.start,
children: [
Text(
video.name ?? '',
maxLines: 1,
overflow:
TextOverflow.ellipsis,
style: TextStyle(
fontSize: 13.sp,
color: Color(0xFF1E1E20),
fontWeight:
FontWeight.w500,
),
),
SizedBox(height: 6.w),
Text(
video.category?.first ?? '',
maxLines: 1,
overflow:
TextOverflow.ellipsis,
style: TextStyle(
fontSize: 10.sp,
color: Color(0xFF79C900),
fontWeight:
FontWeight.w400,
),
),
SizedBox(height: 20.w),
Row(
children: [
SizedBox(
width: 26.w,
height: 26.w,
child: CircularProgressIndicator(
value:
(video.currentEpisode ??
0) /
(video.episodeTotal! >
0
? video
.episodeTotal!
: 1),
backgroundColor:
Color(0xFFD9D9D9),
valueColor:
AlwaysStoppedAnimation<
Color
>(
Color(
0xFFA7F62F,
),
),
strokeWidth: 3.w,
),
),
),
strokeWidth: 3.w,
SizedBox(width: 5.w),
Text(
"${((video.currentEpisode ?? 0) / (video.episodeTotal! > 0 ? video.episodeTotal! : 1) * 100).toStringAsFixed(0)}%",
style: TextStyle(
fontSize: 12.sp,
color: Color(
0xFF1E1E20,
),
fontWeight:
FontWeight.w400,
),
),
],
),
),
SizedBox(width: 5.w),
Text(
"${((video.currentEpisode ?? 0) / (video.episodeTotal! > 0 ? video.episodeTotal! : 1) * 100).toStringAsFixed(0)}%",
style: TextStyle(
fontSize: 12.sp,
color: Color(
0xFF1E1E20,
),
fontWeight:
FontWeight.w400,
],
),
),
const Spacer(),
GestureDetector(
onTap: () =>
logic.likeVideo(video),
child: Column(
children: [
Image.asset(
video.isCollect == 1
? 'ic_collect_sel.png'
.ktIcon
: 'ic_collect_unsel.png'
.ktIcon,
width: 32.w,
),
),
],
],
),
),
],
),
),
const Spacer(),
GestureDetector(
onTap: () =>
logic.likeVideo(video),
child: Column(
children: [
Image.asset(
video.isCollect == 1
? 'ic_collect_sel.png'
.ktIcon
: 'ic_collect_unsel.png'
.ktIcon,
width: 32.w,
),
],
),
),
],
),
);
},
separatorBuilder: (_, __) =>
SizedBox(height: 12.w),
itemCount: state.historyList.length,
),
);
},
separatorBuilder: (_, __) =>
SizedBox(height: 12.w),
itemCount: state.historyList.length,
),
],
),
),
],
),
),
],
),
),
],
),
),
);
},
);

View File

@ -0,0 +1,259 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import 'package:get/get.dart';
import 'package:card_swiper/card_swiper.dart';
import '../../dio_cilent/kt_apis.dart';
import '../../dio_cilent/kt_request.dart';
import '../../kt_model/kt_receive_coin_info.dart';
import '../../kt_model/kt_short_video_bean.dart';
import '../../kt_utils/kt_toast_utils.dart';
import '../../kt_utils/kt_string_extend.dart';
import '../../kt_widgets/kt_network_image.dart';
import '../kt_mine/kt_store/logic.dart';
import '../kt_routes.dart';
import 'state.dart';
class KtRefillLogic extends GetxController {
final state = KtRefillState();
final RefreshController refreshController = RefreshController();
@override
void onReady() {
super.onReady();
getReceiveDayCoin();
getRefillInfo();
}
@override
void onClose() {
super.onClose();
refreshController.dispose();
}
getReceiveDayCoin() async {
ApiResponse res = await KtHttpClient().request(
KtApis.getReceiveDayCoin,
method: HttpMethod.get,
);
if (res.success) {
state.hasSubCoin = res.data['is_exist_sub'] == 1;
update();
}
}
getRefillInfo() async {
try {
ApiResponse res = await KtHttpClient().request(
KtApis.getReceiveDayCoinInfo,
method: HttpMethod.get,
);
if (res.success) {
state.receiveCoinInfo = ReceiveCoinInfo.fromJson(res.data);
update();
refreshController.refreshCompleted();
}
} catch (e) {
refreshController.refreshFailed();
}
}
refreshInfo() {
getRefillInfo();
Get.put(KtStoreLogic()).getStoreInfo();
}
receiveDayCoin({int? id}) async {
Map<String, dynamic> params = {};
if (id != null) params['id'] = id;
ApiResponse res = await KtHttpClient().request(
KtApis.receiveDayCoin,
data: params,
);
if (res.success) {
KtToastUtils.showToast('Success');
getRefillInfo();
getReceiveDayCoin();
getRecommend();
}
}
getRecommend() async {
ApiResponse res = await KtHttpClient().request(
KtApis.getDetailsRecommand,
method: HttpMethod.get,
);
if (res.success) {
state.recommendList = [
...res.data['list'].map((item) => KtShortVideoBean.fromJson(item)),
];
if (state.recommendList.isNotEmpty) {
showRecommendDialog();
}
update();
}
}
showRecommendDialog() {
EasyThrottle.throttle('show-recommend', Duration(seconds: 3), () async {
Get.bottomSheet(
isScrollControlled: true,
isDismissible: true,
enableDrag: false,
Stack(
children: [
Positioned(
bottom: 0,
left: 0,
child: Container(
height: 503.w,
width: ScreenUtil().screenWidth,
padding: EdgeInsets.fromLTRB(0.w, 75.w, 0.w, 20.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('video_recommend_bg.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Column(
children: [
Row(
children: [
SizedBox(width: 15.w),
Image.asset('ip.png'.ktIcon, width: 38.w, height: 40.w),
SizedBox(width: 6.w),
Container(
height: 30.w,
padding: EdgeInsets.symmetric(horizontal: 14.w),
decoration: BoxDecoration(
color: Color(0xFF1E1E20),
borderRadius: BorderRadius.circular(15.w),
),
child: Center(
child: Text(
'More Drama Gold Below!',
style: TextStyle(
color: Color(0xFFA7F62F),
fontSize: 16.sp,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
SizedBox(
height: 290.w,
child: Swiper(
layout: SwiperLayout.CUSTOM,
customLayoutOption:
CustomLayoutOption(startIndex: -1, stateCount: 3)
..addRotate([-20.0 / 180, 0.0, 20.0 / 180])
..addTranslate([
Offset(-220.w, 0),
Offset(0, 0),
Offset(220.w, 0),
]),
itemWidth: 190.w,
itemHeight: 226.w,
itemBuilder: (context, index) {
final item =
state.recommendList[index %
state.recommendList.length];
return GestureDetector(
onTap: () {
Get.back();
Get.toNamed(
KtRoutes.shortVideo,
arguments: {
'shortPlayId':
state.recommendList[index].shortPlayId,
'imageUrl':
state.recommendList[index].imageUrl ?? '',
},
);
},
child: KtNetworkImage(
imageUrl: item.imageUrl ?? '',
width: 190.w,
height: 226.w,
borderRadius: BorderRadius.circular(20.w),
),
);
},
itemCount: state.recommendList.length,
loop: true,
autoplay: true,
onIndexChanged: (index) => state.recommendIndex = index,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
Get.back();
Get.toNamed(
KtRoutes.shortVideo,
arguments: {
'shortPlayId': state
.recommendList[state.recommendIndex]
.shortPlayId,
'imageUrl':
state
.recommendList[state.recommendIndex]
.imageUrl ??
'',
},
);
},
child: Container(
width: 280.w,
height: 64.w,
padding: EdgeInsets.only(bottom: 16.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'video_recommend_btn.png'.ktIcon,
),
fit: BoxFit.cover,
),
),
child: Container(
height: 48.w,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'video_recommend_play.png'.ktIcon,
width: 18.w,
height: 18.w,
),
SizedBox(width: 4.w),
Text(
'watch now',
style: TextStyle(
color: Colors.black,
fontSize: 14.sp,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
),
],
),
],
),
),
),
],
),
);
});
}
}

View File

@ -0,0 +1,12 @@
import 'package:flutter_kinetra/kt_model/kt_receive_coin_info.dart';
import 'package:flutter_kinetra/kt_model/kt_short_video_bean.dart';
class KtRefillState {
KtRefillState();
ReceiveCoinInfo? receiveCoinInfo;
bool hasSubCoin = false;
List<KtShortVideoBean> recommendList = [];
int recommendIndex = 0;
}

View File

@ -0,0 +1,772 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh/pull_to_refresh.dart';
import '../../kt_model/kt_receive_coin_info.dart';
import '../../kt_model/kt_goods_bean.dart';
import '../../kt_utils/kt_string_extend.dart';
import '../kt_mine/kt_store/logic.dart';
import 'logic.dart';
class KtRefillPage extends StatefulWidget {
const KtRefillPage({super.key});
@override
State<KtRefillPage> createState() => _KtRefillPageState();
}
class _KtRefillPageState extends State<KtRefillPage> {
final logic = Get.put(KtRefillLogic());
final state = Get.find<KtRefillLogic>().state;
@override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
body: Container(
padding: EdgeInsets.fromLTRB(
15.w,
ScreenUtil().statusBarHeight,
15.w,
0,
),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('bg1.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
IconButton(
icon: Image.asset('ic_back.png'.ktIcon, width: 10.w),
onPressed: () => Navigator.of(context).maybePop(),
),
Text(
'My Refills',
style: TextStyle(
fontSize: 18.sp,
fontWeight: FontWeight.w600,
color: Colors.black,
),
),
SizedBox(width: 24.w),
],
),
Expanded(
child: GetBuilder<KtRefillLogic>(
builder: (ctrl) {
return SmartRefresher(
controller: logic.refreshController,
physics: const ClampingScrollPhysics(),
enablePullUp: false,
enablePullDown: true,
onRefresh: logic.refreshInfo,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
cardView(),
if (state.hasSubCoin) activeRefillView(),
weeklyRefillView(),
ruleView(),
],
),
),
);
},
),
),
],
),
),
);
}
Widget cardView() {
return Container(
width: 345.w,
height: 258.w,
padding: EdgeInsets.only(top: 59.w, left: 14.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('refills_top_bg.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Rewards Overview',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFF1E1E20),
fontSize: 15.sp,
fontWeight: FontWeight.w600,
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 16.w),
child: Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Weekly Total',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFF5E5E5E),
fontSize: 12.sp,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 4.w),
Row(
children: [
Image.asset('ic_coin.png'.ktIcon, width: 17),
SizedBox(width: 4.w),
Text(
'${state.receiveCoinInfo?.weekMaxTotal ?? 0}',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFFFF9500),
fontSize: 18.sp,
fontWeight: FontWeight.w700,
),
),
],
),
],
),
Container(
margin: EdgeInsets.symmetric(horizontal: 18.w),
width: 1.w,
height: 32.w,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('refills_top_bg_line.png'.ktIcon),
fit: BoxFit.fill,
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Claimable Coins',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFF5E5E5E),
fontSize: 12.sp,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 4.w),
Row(
children: [
Image.asset('ic_coin.png'.ktIcon, width: 17),
SizedBox(width: 4.w),
Text(
'${state.receiveCoinInfo?.weekTotal ?? 0}',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFFFF9500),
fontSize: 18.sp,
fontWeight: FontWeight.w700,
),
),
],
),
],
),
],
),
),
Row(
children: [
Text(
'Active Refills:',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFF777777),
fontSize: 12.sp,
fontWeight: FontWeight.w400,
),
),
Text(
'${state.receiveCoinInfo?.receiveCount ?? 0}',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFF79C900),
fontSize: 12.sp,
fontWeight: FontWeight.w700,
),
),
],
),
SizedBox(height: 6.w),
GestureDetector(
onTap: () {
if (!state.hasSubCoin) return;
logic.receiveDayCoin();
},
child: state.hasSubCoin
? Stack(
children: [
Image.asset(
'refills_top_btn_all.png'.ktIcon,
width: 337.w,
height: 68.w,
),
Container(
height: 46.w,
margin: EdgeInsets.only(top: 5.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Claim All',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFF1E1E20),
fontSize: 15.sp,
fontWeight: FontWeight.w600,
),
),
SizedBox(width: 6.w),
Image.asset('ic_coin.png'.ktIcon, width: 13),
SizedBox(width: 2.w),
Text(
'${state.receiveCoinInfo?.receiveCoins ?? 0}',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFF1E1E20),
fontSize: 14.sp,
fontWeight: FontWeight.w700,
),
),
],
),
),
],
)
: Stack(
children: [
Image.asset(
'refills_top_btn.png'.ktIcon,
width: 337.w,
height: 68.w,
),
Container(
height: 46.w,
margin: EdgeInsets.only(top: 5.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Get a Refill to Claim',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFFF7F7F7),
fontSize: 15.sp,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
),
],
),
);
}
Widget activeRefillView() {
return GetBuilder<KtRefillLogic>(
builder: (ctrl) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 20.w),
Text(
'Active Refills',
style: TextStyle(
fontSize: 15.sp,
fontFamily: 'Inter',
fontWeight: FontWeight.w600,
color: Color(0xFF1C1C1C),
),
),
ListView.builder(
padding: EdgeInsets.only(top: 0.w),
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: state.receiveCoinInfo?.receiveList?.length ?? 2,
itemBuilder: (context, index) {
ReceiveList? item = state.receiveCoinInfo?.receiveList?[index];
return Container(
width: 345.w,
height: 122.w,
padding: EdgeInsets.only(top: 14.w, left: 12.w),
margin: EdgeInsets.only(top: 10.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('refills_center_bg-s.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
item?.title ?? '',
style: TextStyle(
fontFamily: 'Inter',
fontSize: 14.sp,
fontWeight: FontWeight.w700,
color: Colors.white,
),
),
SizedBox(width: 8.w),
Text(
'(Day ${item?.dayText ?? ''}/7)',
style: TextStyle(
fontFamily: 'Inter',
fontSize: 14.sp,
fontWeight: FontWeight.w400,
color: Colors.white.withValues(alpha: 0.70),
),
),
],
),
Container(
margin: EdgeInsets.symmetric(vertical: 12.w),
width: 320.w,
height: 1.w,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('refills_center_line.png'.ktIcon),
fit: BoxFit.fill,
),
),
),
Row(
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Total Reward',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFFCAB7A9),
fontSize: 12.sp,
fontWeight: FontWeight.w700,
),
),
SizedBox(height: 8.w),
Row(
children: [
Image.asset('ic_coin.png'.ktIcon, width: 14),
SizedBox(width: 4.w),
Text(
'${item?.weekMaxTotal ?? 0}',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFFFFF9E8),
fontSize: 14.sp,
fontWeight: FontWeight.w700,
),
),
],
),
],
),
Container(
margin: EdgeInsets.symmetric(horizontal: 12.w),
width: 1.w,
height: 32.w,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'refills_center_line2.png'.ktIcon,
),
fit: BoxFit.fill,
),
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Remaining',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFFCAB7A9),
fontSize: 12.sp,
fontWeight: FontWeight.w700,
),
),
SizedBox(height: 8.w),
Row(
children: [
Image.asset('ic_coin.png'.ktIcon, width: 14),
SizedBox(width: 4.w),
Text(
'${item?.weekRemainingTotal ?? 0}',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFFFFF9E8),
fontSize: 14.sp,
fontWeight: FontWeight.w700,
),
),
],
),
],
),
const Spacer(),
GestureDetector(
onTap: () {
if (item?.receiveCoins == 0) return;
logic.receiveDayCoin(id: item!.id);
},
child: item?.receiveCoins == 0
? Container(
width: 100.w,
height: 48.w,
decoration: BoxDecoration(
color: Color(0xE5B1B1B1),
borderRadius: BorderRadius.all(
Radius.circular(14.w),
),
),
child: Center(
child: Text(
'Claimed',
style: TextStyle(
color: Color(0xFFF7F7F7),
fontSize: 14.sp,
fontWeight: FontWeight.w600,
),
),
),
)
: Stack(
children: [
Container(
width: 100.w,
height: 48.w,
padding: EdgeInsets.only(top: 5.w),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
const Color(0xFF986100),
const Color(0xFF322000),
],
),
border: Border.all(
width: 0.5.w,
color: Colors.white,
),
borderRadius: BorderRadius.all(
Radius.circular(14.w),
),
),
child: Column(
children: [
Text(
'Claim',
style: TextStyle(
fontFamily: 'Inter',
color: Colors.white,
fontSize: 14.sp,
fontWeight: FontWeight.w700,
),
),
Row(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Image.asset(
'ic_coin.png'.ktIcon,
width: 14,
),
SizedBox(width: 4.w),
Text(
'${item?.receiveCoins ?? 0}',
style: TextStyle(
fontFamily: 'Inter',
color: Colors.white
.withValues(
alpha: 0.70,
),
fontSize: 12.sp,
fontWeight: FontWeight.w600,
),
),
],
),
],
),
),
Positioned(
left: -6.w,
top: -6.w,
child: Image.asset(
'refills_center_star.png'.ktIcon,
width: 20.w,
),
),
Positioned(
right: -6.w,
bottom: -6.w,
child: Image.asset(
'refills_center_star.png'.ktIcon,
width: 20.w,
),
),
],
),
),
SizedBox(width: 12.w),
],
),
],
),
);
},
),
],
);
},
);
}
Widget weeklyRefillView() {
Get.put(KtStoreLogic());
return GetBuilder<KtStoreLogic>(
builder: (ctrl) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 20.w),
if (ctrl.state.refillCoinList.isNotEmpty)
Text(
'Weekly Refill',
style: TextStyle(
fontFamily: 'Inter',
fontSize: 15.sp,
fontWeight: FontWeight.w600,
color: Color(0xFF1C1C1C),
),
),
ListView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
itemCount: ctrl.state.refillCoinList.length,
itemBuilder: (context, index) {
KtGoodsBean? item = ctrl.state.refillCoinList[index];
return GestureDetector(
onTap: () => ctrl.buyGoods(item),
child: Container(
width: 345.w,
height: 80.w,
padding: EdgeInsets.only(left: 16.w, right: 13.w),
margin: EdgeInsets.only(top: 10.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('refills_center_bg.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Weekly Refill',
style: TextStyle(
fontFamily: 'Inter',
color: Colors.white,
fontSize: 14.sp,
fontWeight: FontWeight.w700,
),
),
SizedBox(height: 5.w),
Row(
children: [
Image.asset('ic_coin.png'.ktIcon, width: 16),
SizedBox(width: 4.w),
Text(
'${item.extInfo?.maxTotalCoins ?? 0}',
style: TextStyle(
fontFamily: 'Inter',
color: Color(0xFFFFF9E8),
fontSize: 18.sp,
fontWeight: FontWeight.w600,
),
),
SizedBox(width: 4.w),
Container(
height: 18.w,
padding: EdgeInsets.symmetric(
horizontal: 8.w,
),
decoration: BoxDecoration(
color: Colors.white.withValues(alpha: 0.15),
borderRadius: BorderRadius.all(
Radius.circular(4.w),
),
),
child: Text(
item.extInfo?.receiveCoinsRate ?? '',
style: TextStyle(
fontFamily: 'Inter',
color: Colors.white,
fontSize: 12.sp,
fontWeight: FontWeight.w400,
),
),
),
],
),
],
),
Stack(
children: [
Container(
constraints: BoxConstraints(minWidth: 90.w),
height: 48.w,
padding: EdgeInsets.only(
top: 5.w,
left: 15.w,
right: 15.w,
),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
const Color(0xFF986100),
const Color(0xFF322000),
],
),
border: Border.all(
width: 0.5.w,
color: Colors.white,
),
borderRadius: BorderRadius.all(
Radius.circular(14.w),
),
),
child: Column(
children: [
Text(
item.productDetails?.price ?? '',
style: TextStyle(
fontFamily: 'Inter',
height: 1,
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.w700,
),
),
Text(
'/week',
style: TextStyle(
fontFamily: 'Inter',
color: Colors.white.withValues(
alpha: 0.50,
),
fontSize: 12.sp,
fontWeight: FontWeight.w400,
),
),
],
),
),
Positioned(
left: -6.w,
top: -6.w,
child: Image.asset(
'refills_center_star.png'.ktIcon,
width: 20.w,
),
),
Positioned(
right: -6.w,
bottom: -6.w,
child: Image.asset(
'refills_center_star.png'.ktIcon,
width: 20.w,
),
),
],
),
],
),
),
);
},
),
],
);
},
);
}
Widget ruleView() {
return Container(
margin: EdgeInsets.only(top: 10.w, bottom: 43.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Subscription Rules',
style: TextStyle(
fontFamily: 'Inter',
fontSize: 14.sp,
fontWeight: FontWeight.w600,
color: Color(0xFF94949B),
),
),
SizedBox(height: 6.w),
...[
'1.Up to 2 subscriptions can be active at once.',
'2.Coins are delivered instantly upon purchase.',
'3.Daily bonus coins available from the next day.',
'4.All coins will be revoked when the subscription expires,',
'including both initial and daily coins.',
].map(
(text) => Container(
margin: EdgeInsets.only(bottom: 4.w),
child: Text(
text,
style: TextStyle(
fontFamily: 'Inter',
fontSize: 12.sp,
fontWeight: FontWeight.w400,
color: Color(0xFF94949B),
),
),
),
),
],
),
);
}
}

View File

@ -11,7 +11,7 @@ import 'kt_mine/kt_store/view.dart';
import 'kt_short_video/view.dart';
import 'kt_splash_page.dart';
import 'kt_webview_page.dart';
import 'kt_refill/view.dart';
///
class KtRoutes {
static const String splash = '/';
@ -52,5 +52,6 @@ class KtRoutes {
name: webView,
page: () => const KtWebViewPage(url: 'url'),
),
GetPage(name: refill, page: () => const KtRefillPage()),
];
}

View File

@ -1,12 +1,15 @@
import 'package:easy_debounce/easy_throttle.dart';
import 'dart:io';
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:card_swiper/card_swiper.dart';
import 'package:flutter_kinetra/kt_pages/kt_mine/logic.dart';
import 'package:flutter_kinetra/kt_pages/kt_short_video/state.dart';
import 'package:flutter_kinetra/kt_utils/kt_string_extend.dart';
import 'package:flutter_kinetra/kt_utils/kt_toast_utils.dart';
import 'package:flutter_kinetra/kt_pages/kt_home/logic.dart';
import 'package:flutter_kinetra/kt_pages/kt_my_list/logic.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:video_player/video_player.dart';
@ -14,10 +17,13 @@ import 'package:video_player/video_player.dart';
import '../../dio_cilent/kt_apis.dart';
import '../../dio_cilent/kt_request.dart';
import '../../kt_model/kt_video_detail_bean.dart';
import '../../kt_model/kt_short_video_bean.dart';
import '../../kt_utils/kt_device_info_utils.dart';
import '../../kt_widgets/kt_status_widget.dart';
import '../../kt_widgets/kt_store_widget.dart';
import '../../kt_widgets/kt_dialog.dart';
import '../kt_mine/kt_store/logic.dart';
import '../../kt_widgets/kt_network_image.dart';
class VideoPlayLogic extends GetxController {
final state = VideoPlayState();
@ -30,6 +36,9 @@ class VideoPlayLogic extends GetxController {
bool _disposed = false;
final userLogic = Get.put(KtMineLogic());
final mylistLogic = Get.put(MyListLogic());
final mylistState = Get.find<MyListLogic>().state;
@override
void onInit() {
super.onInit();
@ -45,16 +54,44 @@ class VideoPlayLogic extends GetxController {
state.imageUrl = Get.arguments['imageUrl'] ?? '';
state.activityId = Get.arguments['activityId'];
state.isFromDiscover = Get.arguments['isFromDiscover'] ?? false;
startTimer();
fetchData();
getRecommend();
startVideoTimer();
}
@override
void onClose() {
_disposed = true;
clearCacheCtrl();
state.timer?.cancel();
state.videotimer?.cancel();
super.onClose();
}
//
initData(shortPlayId, imageUrl) {
uploadHistorySeconds(
controllers[currentIndex]?.value.position.inMilliseconds ?? 0,
);
Get.back();
state.shortPlayId = shortPlayId;
state.videoId = 0;
state.imageUrl = imageUrl;
state.isRecommend = false;
// pageController.dispose();
currentIndex = 0;
pageController.jumpToPage(currentIndex);
// pageController = PageController(initialPage: currentIndex);
clearCacheCtrl();
fetchData();
state.recommendList.clear();
getRecommend();
startTimer();
startVideoTimer();
}
clearCacheCtrl() {
for (var controller in controllers) {
controller?.pause();
@ -88,6 +125,8 @@ class VideoPlayLogic extends GetxController {
state.videoId = state.video?.videoInfo?.shortPlayVideoId ?? 0;
}
state.episodeList = state.video?.episodeList ?? [];
state.videoFirstTime =
int.tryParse(state.video?.videoInfo?.playSeconds ?? '0') ?? 0;
if (toPage) currentIndex = (state.video?.videoInfo?.episode ?? 1) - 1;
for (var video in state.episodeList) {
if (video.isLock == true) {
@ -105,6 +144,7 @@ class VideoPlayLogic extends GetxController {
currentIndex,
isToggle: true,
isUploadHistorySeconds: false,
type: 1,
);
} else {
_initializeController(currentIndex);
@ -123,6 +163,37 @@ class VideoPlayLogic extends GetxController {
}
}
// 5s之后返回推荐
void startTimer() {
state.timer = Timer(const Duration(seconds: 5), () {
state.isRecommend = true;
update();
});
}
// 5s
void startVideoTimer() {
state.videotimer?.cancel();
state.videotimer = Timer(const Duration(seconds: 5), () {
state.isVideoctrHide = true;
update();
});
}
//
getRecommend() async {
ApiResponse res = await KtHttpClient().request(
KtApis.getDetailsRecommand,
method: HttpMethod.get,
);
if (res.success) {
state.recommendList = [
...res.data['list'].map((item) => KtShortVideoBean.fromJson(item)),
];
update();
}
}
void reportHistory() {
if (currentIndex < 0 || currentIndex >= state.episodeList.length) return;
Map<String, dynamic> params = {
@ -141,6 +212,19 @@ class VideoPlayLogic extends GetxController {
KtHttpClient().request(KtApis.activeAfterWatchingVideo, data: params);
}
void updateHomeVideo() {
final homeLogic = Get.put(KtHomeLogic());
final homeState = Get.find<KtHomeLogic>().state;
int playTime = controllers[currentIndex]?.value.position.inSeconds ?? 0;
homeState.curVideo = KtShortVideoBean()
..shortPlayId = state.shortPlayId
..imageUrl = state.video?.shortPlayInfo?.imageUrl
..name = state.video?.shortPlayInfo?.name
..playTime = playTime
..process = currentIndex + 1;
homeLogic.update();
}
//
Future<void> onPageChanged(
int index, {
@ -177,6 +261,9 @@ class VideoPlayLogic extends GetxController {
});
}
currentIndex = index;
if (type == 1) {
controllers[index]?.seekTo(Duration(milliseconds: state.videoFirstTime));
}
if (state.episodeList[index].isLock == true) {
controllers[index]?.seekTo(Duration(seconds: 0));
controllers[index]?.pause();
@ -199,8 +286,9 @@ class VideoPlayLogic extends GetxController {
controllers[index]?.play();
}
}
controllers[index]?.setPlaybackSpeed(state.currentSpeed);
// updateHomeVideo();
updateHomeVideo();
// print('----curIndex:$currentIndex');
//
_preloadAdjacentVideos();
@ -241,7 +329,8 @@ class VideoPlayLogic extends GetxController {
try {
await controller.initialize();
if (index == currentIndex && (episode.isLock == false || userLogic.state.userInfo.isVip == true)) {
if (index == currentIndex &&
(episode.isLock == false || userLogic.state.userInfo.isVip == true)) {
controller.play();
update();
}
@ -251,7 +340,7 @@ class VideoPlayLogic extends GetxController {
if (currentIndex == index && !_disposed) update();
if (currentIndex == state.episodeList.length - 1 &&
(controllers.last?.value.isCompleted ?? false)) {
// showRecommendDialog();
showRecommendDialog();
}
if (controller.value.isCompleted && !controller.value.isBuffering) {
onPageChanged(index + 1, isToggle: true);
@ -291,20 +380,21 @@ class VideoPlayLogic extends GetxController {
Future<void> likeVideo() async {
if (state.video == null) return;
Map<String, dynamic> params = {
"short_play_id": state.video?.shortPlayInfo?.shortPlayId,
"video_id": state.episodeList[currentIndex].id,
};
if (state.video?.shortPlayInfo?.isCollect ?? false) {
await KtHttpClient().request(KtApis.deleteFavoriteVideo, data: params);
// await KtHttpClient().request(KtApis.deleteFavoriteVideo, data: params);
cancelCollect(state.video!.shortPlayInfo!.shortPlayId!);
} else {
await KtHttpClient().request(KtApis.collectVideo, data: params);
state.video?.shortPlayInfo?.isCollect =
!(state.video?.shortPlayInfo?.isCollect ?? false);
update();
mylistLogic.getCollectList(refresh: true);
mylistLogic.getHistoryList(refresh: true);
}
state.video?.shortPlayInfo?.isCollect =
!(state.video?.shortPlayInfo?.isCollect ?? false);
update();
}
//
@ -428,4 +518,202 @@ class VideoPlayLogic extends GetxController {
),
);
}
cancelCollect(num id) async {
Get.dialog(
KtDialog(
title: 'Remove from your list?',
subTitle: 'This drama will be removed from your saved list.',
rightBtnText: 'Remove',
rightBtnIcon: 'ic_dialog_delete.png',
rightBtnFunc: () async {
ApiResponse res = await KtHttpClient().request(
KtApis.deleteFavoriteVideo,
queryParameters: {'short_play_id': id},
);
if (res.success) {
state.video?.shortPlayInfo?.isCollect =
!(state.video?.shortPlayInfo?.isCollect ?? false);
update();
// state.favoriteList.removeWhere((item) => id == item.shortPlayId);
mylistState.chestList.removeWhere((item) => id == item.shortPlayId);
mylistState.historyList
.firstWhereOrNull((item) => id == item.shortPlayId)
?.isCollect =
0;
mylistLogic.update();
}
},
),
);
}
showRecommendDialog() {
EasyThrottle.throttle('show-recommend', Duration(seconds: 3), () async {
controllers[currentIndex]?.pause();
Get.bottomSheet(
isScrollControlled: true,
isDismissible: false,
enableDrag: false,
Stack(
children: [
Positioned(
top: ScreenUtil().statusBarHeight + 10.w,
left: 16.w,
child: GestureDetector(
onTap: () {
EasyThrottle.throttle(
'back-recommend',
Duration(seconds: 3),
() async {
Get.back();
Get.back();
},
);
},
child: Image.asset('ic_back_white.png'.ktIcon, width: 24.w),
),
),
Positioned(
bottom: 0,
left: 0,
child: Container(
height: 503.w,
width: ScreenUtil().screenWidth,
padding: EdgeInsets.fromLTRB(0.w, 75.w, 0.w, 20.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('video_recommend_bg.png'.ktIcon),
fit: BoxFit.fill,
),
),
child: Column(
children: [
Row(
children: [
SizedBox(width: 15.w),
Image.asset('ip.png'.ktIcon, width: 38.w, height: 40.w),
SizedBox(width: 6.w),
Container(
height: 30.w,
padding: EdgeInsets.symmetric(horizontal: 14.w),
decoration: BoxDecoration(
color: Color(0xFF1E1E20),
borderRadius: BorderRadius.circular(15.w),
),
child: Center(
child: Text(
'More Drama Gold Below!',
style: TextStyle(
color: Color(0xFFA7F62F),
fontSize: 16.sp,
fontWeight: FontWeight.w500,
),
),
),
),
],
),
SizedBox(
height: 290.w,
child: Swiper(
layout: SwiperLayout.CUSTOM,
customLayoutOption:
CustomLayoutOption(startIndex: -1, stateCount: 3)
..addRotate([-20.0 / 180, 0.0, 20.0 / 180])
..addTranslate([
Offset(-220.w, 0),
Offset(0, 0),
Offset(220.w, 0),
]),
itemWidth: 190.w,
itemHeight: 226.w,
itemBuilder: (context, index) {
final item =
state.recommendList[index %
state.recommendList.length];
return GestureDetector(
onTap: () {
initData(
state.recommendList[index].shortPlayId ?? -1,
state.recommendList[index].imageUrl ?? '',
);
},
child: KtNetworkImage(
imageUrl: item.imageUrl ?? '',
width: 190.w,
height: 226.w,
borderRadius: BorderRadius.circular(20.w),
),
);
},
itemCount: state.recommendList.length,
loop: true,
autoplay: true,
onIndexChanged: (index) => state.recommendIndex = index,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
initData(
state
.recommendList[state.recommendIndex]
.shortPlayId ??
-1,
state
.recommendList[state.recommendIndex]
.imageUrl ??
'',
);
},
child: Container(
width: 280.w,
height: 64.w,
padding: EdgeInsets.only(bottom: 16.w),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'video_recommend_btn.png'.ktIcon,
),
fit: BoxFit.cover,
),
),
child: Container(
height: 48.w,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
'video_recommend_play.png'.ktIcon,
width: 18.w,
height: 18.w,
),
SizedBox(width: 4.w),
Text(
'watch now',
style: TextStyle(
color: Colors.black,
fontSize: 14.sp,
fontWeight: FontWeight.w500,
),
),
],
),
),
),
),
],
),
],
),
),
),
],
),
);
});
}
}

View File

@ -1,10 +1,14 @@
import 'dart:async';
import 'package:flutter/material.dart';
import '../../kt_model/kt_video_detail_bean.dart';
import '../../kt_widgets/kt_status_widget.dart';
import '../../kt_model/kt_short_video_bean.dart';
class VideoPlayState {
String imageUrl = '';
int shortPlayId = -1;
num shortPlayId = -1;
num videoId = -1;
int videoFirstTime = 0;
int curUnlock = 999;
int? activityId;
bool isFromDiscover = false;
@ -15,4 +19,12 @@ class VideoPlayState {
int currentVideoIndex = 0;
double currentSpeed = 1.0;
final List<double> speedList = [0.75, 1.0, 1.25, 1.5, 2.0, 3.0];
List<KtShortVideoBean> recommendList = [];
int recommendIndex = 0;
bool isRecommend = false;
Timer? timer;
bool isVideoctrHide = false;
Timer? videotimer;
}

View File

@ -32,6 +32,7 @@ class _VideoPlayPageState extends State<VideoPlayPage>
@override
void initState() {
state.imageUrl = Get.arguments['imageUrl'] ?? '';
// state.isFromRecommend = Get.arguments['isFromRecommend'] ?? false;
super.initState();
}
@ -54,9 +55,16 @@ class _VideoPlayPageState extends State<VideoPlayPage>
return PopScope(
canPop: true,
onPopInvokedWithResult: (didPop, result) async {
// if (logic.controllers.isNotEmpty) {
// logic.uploadHistorySeconds(logic.controllers[logic.currentIndex]?.value.position.inMilliseconds ?? 0);
// }
if (logic.controllers.isNotEmpty) {
logic.uploadHistorySeconds(
logic
.controllers[logic.currentIndex]
?.value
.position
.inMilliseconds ??
0,
);
}
if (didPop) return;
},
child: Scaffold(
@ -206,9 +214,22 @@ class _VideoPlayPageState extends State<VideoPlayPage>
GestureDetector(
onTap: () {
if (episode.isLock == true) return;
(controller?.value.isPlaying ?? true)
? controller?.pause()
: controller?.play();
if (controller?.value.isPlaying ?? true) {
if (state.isVideoctrHide) {
state.isVideoctrHide = false;
logic.startVideoTimer();
} else {
controller?.pause();
state.videotimer?.cancel();
}
} else {
logic.startVideoTimer();
controller?.play();
}
// (controller?.value.isPlaying ?? true)
// ? controller?.pause()
// : controller?.play();
setState(() {});
},
child: Container(
@ -298,123 +319,126 @@ class _VideoPlayPageState extends State<VideoPlayPage>
),
],
),
//
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),
],
),
if (!state.isVideoctrHide) ...[
//
Positioned(
top: ScreenUtil().statusBarHeight + 10.w,
left: 16.w,
child: GestureDetector(
onTap: () {
if (!state.isRecommend || state.recommendList.isEmpty) {
Get.back();
} else {
logic.showRecommendDialog();
}
},
child: Image.asset('ic_back_white.png'.ktIcon, width: 24.w),
),
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,
),
),
),
//
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),
],
),
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,
),
),
],
),
),
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,
),
),
GetBuilder<VideoPlayLogic>(
id: 'video-like',
builder: (c) {
return GestureDetector(
onTap: () => logic.likeVideo(),
child: Column(
),
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(
state.video?.shortPlayInfo?.isCollect == true
? 'ic_collect_sel.png'.ktIcon
: 'ic_collect_unsel.png'.ktIcon,
width: 32.w,
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,
),
],
),
);
},
),
],
),
],
),
),
),
),
],
],
);
}

View File

@ -5,7 +5,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.1+1
environment:
sdk: ^3.8.1
sdk: ^3.8.0
dependencies:
flutter: