一期功能
@ -1,8 +1,20 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.AD_ID" />
|
||||
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
|
||||
<application
|
||||
android:label="Kinetra"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@mipmap/launcher_icon">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
BIN
android/app/src/main/res/mipmap-hdpi/launcher_icon.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/launcher_icon.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
assets/collect_bg.png
Normal file
After Width: | Height: | Size: 7.6 KiB |
BIN
assets/dialog_bg1.png
Normal file
After Width: | Height: | Size: 225 KiB |
BIN
assets/dialog_bg2.png
Normal file
After Width: | Height: | Size: 261 KiB |
BIN
assets/dialog_delete_account.png
Normal file
After Width: | Height: | Size: 81 KiB |
BIN
assets/dialog_logout.png
Normal file
After Width: | Height: | Size: 99 KiB |
BIN
assets/dialog_star.png
Normal file
After Width: | Height: | Size: 47 KiB |
BIN
assets/dialog_update.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
assets/ep_sel_bg.png
Normal file
After Width: | Height: | Size: 300 KiB |
BIN
assets/explore_bg.png
Normal file
After Width: | Height: | Size: 77 KiB |
BIN
assets/fonts/Inter/Inter-Black-3.otf
Executable file
BIN
assets/fonts/Inter/Inter-Bold-4.otf
Executable file
BIN
assets/fonts/Inter/Inter-ExtraBold-5.otf
Executable file
BIN
assets/fonts/Inter/Inter-ExtraLight-6.otf
Executable file
BIN
assets/fonts/Inter/Inter-Light-7.otf
Executable file
BIN
assets/fonts/Inter/Inter-Medium-8.otf
Executable file
BIN
assets/fonts/Inter/Inter-Regular-9.otf
Executable file
BIN
assets/fonts/Inter/Inter-SemiBold-10.otf
Executable file
BIN
assets/fonts/Inter/Inter-Thin-11.otf
Executable file
BIN
assets/fonts/Inter/Inter-var-2.ttf
Executable file
BIN
assets/home_item_sel.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
assets/ic_arrow_top.png
Normal file
After Width: | Height: | Size: 238 B |
BIN
assets/ic_back.png
Normal file
After Width: | Height: | Size: 579 B |
BIN
assets/ic_back_white.png
Normal file
After Width: | Height: | Size: 410 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
assets/ic_dialog_delete.png
Normal file
After Width: | Height: | Size: 833 B |
BIN
assets/ic_drop_dot.png
Normal file
After Width: | Height: | Size: 942 B |
BIN
assets/ic_ep.png
Normal file
After Width: | Height: | Size: 678 B |
BIN
assets/ic_explore_ip.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
assets/ic_explore_tv.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/ic_favorite.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/ic_fire.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/ic_image_ver.png
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/ic_load_failed.png
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
assets/ic_logo.png
Normal file
After Width: | Height: | Size: 2.2 MiB |
BIN
assets/ic_mine_ip.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
assets/ic_mine_top_bg.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/ic_no_network.png
Normal file
After Width: | Height: | Size: 58 KiB |
BIN
assets/ic_not_found.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
assets/ic_nothing.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
assets/ic_play.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
assets/ic_right_arrow.png
Normal file
After Width: | Height: | Size: 455 B |
BIN
assets/ic_right_white.png
Normal file
After Width: | Height: | Size: 223 B |
BIN
assets/ic_rise_bottom.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
assets/ic_rise_play.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/ic_rise_right.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/ic_top_play.png
Normal file
After Width: | Height: | Size: 610 B |
BIN
assets/setting_bg.png
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
assets/text_bg_discover.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/top_1.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/top_2.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
assets/top_3.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
assets/top_bg.png
Normal file
After Width: | Height: | Size: 141 KiB |
BIN
assets/top_bottom_bg.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
assets/top_other.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
@ -563,7 +563,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
@ -620,7 +620,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
|
@ -1,122 +1 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-20x20@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-29x29@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-40x40@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "60x60",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "Icon-App-60x60@3x.png",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "20x20",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-20x20@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "29x29",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-29x29@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "40x40",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-40x40@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@1x.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "76x76",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-76x76@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "83.5x83.5",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "Icon-App-83.5x83.5@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "1024x1024",
|
||||
"idiom" : "ios-marketing",
|
||||
"filename" : "Icon-App-1024x1024@1x.png",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 324 KiB |
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 7.6 KiB |
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 17 KiB |
@ -1,19 +1,20 @@
|
||||
class KtApis {
|
||||
static const String baseUrl = "https://test1-api.guyantv.com";
|
||||
static const String baseUrl = "https://api-csjiuxi.csjiuxi.com/kinet";
|
||||
// static const String baseUrl = "https://test1-api.guyantv.com";
|
||||
// static const String baseUrl = "https://api-pdaroll.pdaroll.com/panda";
|
||||
|
||||
static const String homeAllModules = "/home/all-modules";
|
||||
static const String newShortPlay = "/newShortPlay";
|
||||
static const String homeVideoList = "/videoList";
|
||||
static const String getCategories = "/getCategories";
|
||||
static const String getMostSearch = "/search/hots";
|
||||
static const String searchHot = "/search/hots";
|
||||
static const String search = "/search";
|
||||
static const String getVideoDetails = "/getVideoDetails";
|
||||
static const String getRecommands = "/getRecommands";
|
||||
static const String getMyHistoryList = "/myHistorys";
|
||||
static const String getMyCollections = "/myCollections";
|
||||
static const String collectVideo = "/collect";
|
||||
static const String cancelCollectVideo = "/cancelCollect";
|
||||
static const String deleteFavoriteVideo = "/cancelCollect";
|
||||
static const String getDetailsRecommand = "/getDetailsRecommand";
|
||||
static const String getRevolutions = "/getRevolutions";
|
||||
|
||||
@ -79,7 +80,7 @@ class KtApis {
|
||||
///青少年互联网文明公约
|
||||
static String WEB_SITE_CIVIZ = "${WEB_SITE_INDEX}civizatio_convention";
|
||||
|
||||
static String WEB_SITE_HOST = "https://campaign.pdaroll.com/";
|
||||
static String WEB_SITE_HOST = "https://campaign.csjiuxi.com/";
|
||||
static String WEB_SITE_ACTIVITY = "${WEB_SITE_HOST}pages/reward/theme3";
|
||||
|
||||
///反馈首页
|
||||
@ -94,6 +95,9 @@ class KtApis {
|
||||
///钱包
|
||||
static String WEB_SITE_WALLET = "${WEB_SITE_HOST}pages/leave/detail";
|
||||
|
||||
///搜索
|
||||
static String WEB_SITE_SEARCH = "${WEB_SITE_HOST}pages/search/kinetra";
|
||||
|
||||
/// w2a
|
||||
static String WEB_2_APP_INDEX = "https://w2a.pdaroll.com/";
|
||||
static String WEB_2_APP_INDEX = "https://w2a.csjiuxi.com/";
|
||||
}
|
||||
|
@ -60,13 +60,13 @@ class ApiException implements Exception {
|
||||
}
|
||||
|
||||
/// Dio请求封装类
|
||||
class HttpClient {
|
||||
static final HttpClient _instance = HttpClient._internal();
|
||||
class KtHttpClient {
|
||||
static final KtHttpClient _instance = KtHttpClient._internal();
|
||||
|
||||
factory HttpClient() => _instance;
|
||||
factory KtHttpClient() => _instance;
|
||||
late Dio _dio;
|
||||
|
||||
HttpClient._internal() {
|
||||
KtHttpClient._internal() {
|
||||
_initDio();
|
||||
}
|
||||
|
||||
|
134
lib/kt_model/kt_history_video_bean.dart
Normal file
@ -0,0 +1,134 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// id : 76
|
||||
/// short_id : 75
|
||||
/// name : "Mrs. Gu, your disguise has been exposed"
|
||||
/// short_play_video_id : 3572
|
||||
/// description : "Since Xu Mumu married Gu Beichen, the family of three has been living their own small and happy life in a low-key manner. But Xu Mumu is not willing to be a full-time housewife from then on and is determined to create her own"
|
||||
/// short_play_id : 76
|
||||
/// image_url : "https://static.wanmwl.com/image/4e123202511fcb209b0d.webp"
|
||||
/// episode_total : 18
|
||||
/// current_episode : 1
|
||||
/// updated_at : "2025-07-04 06:19:21"
|
||||
/// is_collect : 1
|
||||
/// category : ["Dominant CEO Romance","Sweet Romance "]
|
||||
|
||||
KtHistoryVideoBean ktHistoryVideoBeanFromJson(String str) =>
|
||||
KtHistoryVideoBean.fromJson(json.decode(str));
|
||||
String ktHistoryVideoBeanToJson(KtHistoryVideoBean data) =>
|
||||
json.encode(data.toJson());
|
||||
|
||||
class KtHistoryVideoBean {
|
||||
KtHistoryVideoBean({
|
||||
num? id,
|
||||
num? shortId,
|
||||
String? name,
|
||||
num? shortPlayVideoId,
|
||||
String? description,
|
||||
num? shortPlayId,
|
||||
String? imageUrl,
|
||||
num? episodeTotal,
|
||||
num? currentEpisode,
|
||||
String? updatedAt,
|
||||
num? isCollect,
|
||||
List<String>? category,
|
||||
}) {
|
||||
_id = id;
|
||||
_shortId = shortId;
|
||||
_name = name;
|
||||
_shortPlayVideoId = shortPlayVideoId;
|
||||
_description = description;
|
||||
_shortPlayId = shortPlayId;
|
||||
_imageUrl = imageUrl;
|
||||
_episodeTotal = episodeTotal;
|
||||
_currentEpisode = currentEpisode;
|
||||
_updatedAt = updatedAt;
|
||||
_isCollect = isCollect;
|
||||
_category = category;
|
||||
}
|
||||
|
||||
KtHistoryVideoBean.fromJson(dynamic json) {
|
||||
_id = json['id'];
|
||||
_shortId = json['short_id'];
|
||||
_name = json['name'];
|
||||
_shortPlayVideoId = json['short_play_video_id'];
|
||||
_description = json['description'];
|
||||
_shortPlayId = json['short_play_id'];
|
||||
_imageUrl = json['image_url'];
|
||||
_episodeTotal = json['episode_total'];
|
||||
_currentEpisode = json['current_episode'];
|
||||
_updatedAt = json['updated_at'];
|
||||
_isCollect = json['is_collect'];
|
||||
_category = json['category'] != null ? json['category'].cast<String>() : [];
|
||||
}
|
||||
num? _id;
|
||||
num? _shortId;
|
||||
String? _name;
|
||||
num? _shortPlayVideoId;
|
||||
String? _description;
|
||||
num? _shortPlayId;
|
||||
String? _imageUrl;
|
||||
num? _episodeTotal;
|
||||
num? _currentEpisode;
|
||||
String? _updatedAt;
|
||||
num? _isCollect;
|
||||
List<String>? _category;
|
||||
KtHistoryVideoBean copyWith({
|
||||
num? id,
|
||||
num? shortId,
|
||||
String? name,
|
||||
num? shortPlayVideoId,
|
||||
String? description,
|
||||
num? shortPlayId,
|
||||
String? imageUrl,
|
||||
num? episodeTotal,
|
||||
num? currentEpisode,
|
||||
String? updatedAt,
|
||||
num? isCollect,
|
||||
List<String>? category,
|
||||
}) => KtHistoryVideoBean(
|
||||
id: id ?? _id,
|
||||
shortId: shortId ?? _shortId,
|
||||
name: name ?? _name,
|
||||
shortPlayVideoId: shortPlayVideoId ?? _shortPlayVideoId,
|
||||
description: description ?? _description,
|
||||
shortPlayId: shortPlayId ?? _shortPlayId,
|
||||
imageUrl: imageUrl ?? _imageUrl,
|
||||
episodeTotal: episodeTotal ?? _episodeTotal,
|
||||
currentEpisode: currentEpisode ?? _currentEpisode,
|
||||
updatedAt: updatedAt ?? _updatedAt,
|
||||
isCollect: isCollect ?? _isCollect,
|
||||
category: category ?? _category,
|
||||
);
|
||||
num? get id => _id;
|
||||
num? get shortId => _shortId;
|
||||
String? get name => _name;
|
||||
num? get shortPlayVideoId => _shortPlayVideoId;
|
||||
String? get description => _description;
|
||||
num? get shortPlayId => _shortPlayId;
|
||||
String? get imageUrl => _imageUrl;
|
||||
num? get episodeTotal => _episodeTotal;
|
||||
num? get currentEpisode => _currentEpisode;
|
||||
String? get updatedAt => _updatedAt;
|
||||
num? get isCollect => _isCollect;
|
||||
List<String>? get category => _category;
|
||||
|
||||
set isCollect(num? value) => _isCollect = value;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = _id;
|
||||
map['short_id'] = _shortId;
|
||||
map['name'] = _name;
|
||||
map['short_play_video_id'] = _shortPlayVideoId;
|
||||
map['description'] = _description;
|
||||
map['short_play_id'] = _shortPlayId;
|
||||
map['image_url'] = _imageUrl;
|
||||
map['episode_total'] = _episodeTotal;
|
||||
map['current_episode'] = _currentEpisode;
|
||||
map['updated_at'] = _updatedAt;
|
||||
map['is_collect'] = _isCollect;
|
||||
map['category'] = _category;
|
||||
return map;
|
||||
}
|
||||
}
|
37
lib/kt_model/kt_home_category_bean.dart
Normal file
@ -0,0 +1,37 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// category_name : "123"
|
||||
/// category_id : 12
|
||||
|
||||
KtHomeCategoryBean ktHomeCategoryBeanFromJson(String str) =>
|
||||
KtHomeCategoryBean.fromJson(json.decode(str));
|
||||
String ktHomeCategoryBeanToJson(KtHomeCategoryBean data) =>
|
||||
json.encode(data.toJson());
|
||||
|
||||
class KtHomeCategoryBean {
|
||||
KtHomeCategoryBean({String? categoryName, int? categoryId}) {
|
||||
_categoryName = categoryName;
|
||||
_categoryId = categoryId;
|
||||
}
|
||||
|
||||
KtHomeCategoryBean.fromJson(dynamic json) {
|
||||
_categoryName = json['category_name'];
|
||||
_categoryId = json['category_id'];
|
||||
}
|
||||
String? _categoryName;
|
||||
int? _categoryId;
|
||||
KtHomeCategoryBean copyWith({String? categoryName, int? categoryId}) =>
|
||||
KtHomeCategoryBean(
|
||||
categoryName: categoryName ?? _categoryName,
|
||||
categoryId: categoryId ?? _categoryId,
|
||||
);
|
||||
String? get categoryName => _categoryName;
|
||||
int? get categoryId => _categoryId;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['category_name'] = _categoryName;
|
||||
map['category_id'] = _categoryId;
|
||||
return map;
|
||||
}
|
||||
}
|
779
lib/kt_model/kt_video_detail_bean.dart
Normal file
@ -0,0 +1,779 @@
|
||||
import 'dart:convert';
|
||||
|
||||
/// business_model : "iap"
|
||||
/// video_info : {"id":7132,"short_play_video_id":7132,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/3f9afda335fd2616d901/3f9afda335fd2616d901.m3u8","coins":0,"vip_coins":0,"episode":1,"is_vip":2,"play_seconds":"0"}
|
||||
/// shortPlayInfo : {"id":162,"short_id":11,"short_play_id":162,"name":"Madam President, Holding Multiple Positions ","description":"It is rumored outside that the prestigious president of Xu's Group is about to marry the daughter of the Xu family. Just because the name of the Xu family's daughter is Pearl, which is the same as his childhood sweetheart in ","process":2,"image_url":"https://static.wanmwl.com/eyJrZXkiOiJpbWFnZS80ZmRhZDg4OWIxNmM3MmQ2MjkwZC5qcGciLCJlZGl0cyI6eyJyZXNpemUiOnsiZml0IjoiY292ZXIifX19?sign=$23f3feca08db05081465b3677a6f671819b85d9f18ad94cc7236f3c89aada01ebe2de98adbca00c6f0c8c5b8621c5e4f6064afb617","horizontally_img":"https://static.wanmwl.com/image/ac2f87aad3384032215c.jpg","buy_type":1,"tag_type":"","all_coins":0,"collect_total":4,"watch_total":307,"episode_total":18,"search_click_total":0,"is_collect":true,"can_share_get_coin":true,"category":["Sweet Romance "]}
|
||||
/// episodeList : [{"id":7132,"short_play_video_id":7132,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/3f9afda335fd2616d901/3f9afda335fd2616d901.m3u8","coins":0,"vip_coins":0,"episode":1,"is_vip":2,"is_lock":false,"play_seconds":"0"},{"id":7140,"short_play_video_id":7140,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/5c5860a67aab8fe3d89d/5c5860a67aab8fe3d89d.m3u8","coins":0,"vip_coins":0,"episode":2,"is_vip":2,"is_lock":false,"play_seconds":"0"},{"id":7136,"short_play_video_id":7136,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/9f7c98f9c61fdf1bf5fc/9f7c98f9c61fdf1bf5fc.m3u8","coins":0,"vip_coins":0,"episode":3,"is_vip":2,"is_lock":false,"play_seconds":"0"},{"id":7137,"short_play_video_id":7137,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/72c3555f90fcceb9aab4/72c3555f90fcceb9aab4.m3u8","coins":0,"vip_coins":0,"episode":4,"is_vip":2,"is_lock":false,"play_seconds":"0"},{"id":7133,"short_play_video_id":7133,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/171bf35b0196bdf1e839/171bf35b0196bdf1e839.m3u8","coins":0,"vip_coins":0,"episode":5,"is_vip":2,"is_lock":false,"play_seconds":"0"},{"id":7135,"short_play_video_id":7135,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/fff10f96153f53ddc7f6/fff10f96153f53ddc7f6.m3u8","coins":0,"vip_coins":0,"episode":6,"is_vip":2,"is_lock":false,"play_seconds":"0"},{"id":7134,"short_play_video_id":7134,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/9a3ac7c5924b144b6222/9a3ac7c5924b144b6222.m3u8","coins":0,"vip_coins":0,"episode":7,"is_vip":2,"is_lock":false,"play_seconds":"0"},{"id":7139,"short_play_video_id":7139,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/17927e0dd452d5a936d0/17927e0dd452d5a936d0.m3u8","coins":0,"vip_coins":0,"episode":8,"is_vip":2,"is_lock":false,"play_seconds":"0"},{"id":7138,"short_play_video_id":7138,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/1d61d46d30292d0a7269/1d61d46d30292d0a7269.m3u8","coins":0,"vip_coins":0,"episode":9,"is_vip":2,"is_lock":false,"play_seconds":"0"},{"id":7141,"short_play_video_id":7141,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/70ee4deb9bb176846534/70ee4deb9bb176846534.m3u8","coins":0,"vip_coins":0,"episode":10,"is_vip":2,"is_lock":false,"play_seconds":"0"},{"id":7146,"short_play_video_id":7146,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/946ec0930ff6d1f9b36e/946ec0930ff6d1f9b36e.m3u8","coins":69,"vip_coins":0,"episode":11,"is_vip":2,"is_lock":true,"play_seconds":"0"},{"id":7144,"short_play_video_id":7144,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/b92bed7933bbd5916f07/b92bed7933bbd5916f07.m3u8","coins":119,"vip_coins":0,"episode":12,"is_vip":2,"is_lock":true,"play_seconds":"0"},{"id":7142,"short_play_video_id":7142,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/9679216680a679f7e6c2/9679216680a679f7e6c2.m3u8","coins":149,"vip_coins":0,"episode":13,"is_vip":2,"is_lock":true,"play_seconds":"0"},{"id":7143,"short_play_video_id":7143,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/a73214d6aa17af4d639d/a73214d6aa17af4d639d.m3u8","coins":249,"vip_coins":0,"episode":14,"is_vip":2,"is_lock":true,"play_seconds":"0"},{"id":7147,"short_play_video_id":7147,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/3a82aab4550ddc50cfe7/3a82aab4550ddc50cfe7.m3u8","coins":199,"vip_coins":0,"episode":15,"is_vip":2,"is_lock":true,"play_seconds":"0"},{"id":7149,"short_play_video_id":7149,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/0b0781eefb331cc25fee/0b0781eefb331cc25fee.m3u8","coins":159,"vip_coins":0,"episode":16,"is_vip":2,"is_lock":true,"play_seconds":"0"},{"id":7145,"short_play_video_id":7145,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/6c2d042f669e7bdaff3d/6c2d042f669e7bdaff3d.m3u8","coins":69,"vip_coins":0,"episode":17,"is_vip":2,"is_lock":true,"play_seconds":"0"},{"id":7148,"short_play_video_id":7148,"short_play_id":162,"short_id":11,"promise_view_ad":0,"video_url":"https://static.wanmwl.com/videom3u8/d6fd3ab315f161b9b980/d6fd3ab315f161b9b980.m3u8","coins":69,"vip_coins":0,"episode":18,"is_vip":2,"is_lock":true,"play_seconds":"0"}]
|
||||
/// is_collect : true
|
||||
/// show_share_coin : true
|
||||
/// share_coin : 100
|
||||
/// install_coins : 1000
|
||||
/// revolution : 720
|
||||
/// user_level : "normal"
|
||||
/// unlock_video_ad_count : 1
|
||||
/// discount : 80
|
||||
|
||||
VideoDetailBean videoDetailBeanBeanFromJson(String str) =>
|
||||
VideoDetailBean.fromJson(json.decode(str));
|
||||
|
||||
String videoDetailBeanBeanToJson(VideoDetailBean data) =>
|
||||
json.encode(data.toJson());
|
||||
|
||||
class VideoDetailBean {
|
||||
VideoDetailBean({
|
||||
int? payMode,
|
||||
String? businessModel,
|
||||
VideoInfo? videoInfo,
|
||||
ShortPlayInfo? shortPlayInfo,
|
||||
List<Episode>? episodeList,
|
||||
List<int>? checkPoint,
|
||||
bool? isCollect,
|
||||
bool? showShareCoin,
|
||||
num? shareCoin,
|
||||
num? installCoins,
|
||||
dynamic revolution,
|
||||
String? userLevel,
|
||||
num? unlockVideoAdCount,
|
||||
num? discount,
|
||||
}) {
|
||||
_payMode = payMode;
|
||||
_businessModel = businessModel;
|
||||
_videoInfo = videoInfo;
|
||||
_shortPlayInfo = shortPlayInfo;
|
||||
_episodeList = episodeList;
|
||||
_checkPoint = checkPoint;
|
||||
_isCollect = isCollect;
|
||||
_showShareCoin = showShareCoin;
|
||||
_shareCoin = shareCoin;
|
||||
_installCoins = installCoins;
|
||||
_revolution = revolution;
|
||||
_userLevel = userLevel;
|
||||
_unlockVideoAdCount = unlockVideoAdCount;
|
||||
_discount = discount;
|
||||
}
|
||||
|
||||
VideoDetailBean.fromJson(dynamic json) {
|
||||
_businessModel = json['business_model'];
|
||||
_payMode = json['pay_mode'];
|
||||
_videoInfo = json['video_info'] != null
|
||||
? VideoInfo.fromJson(json['video_info'])
|
||||
: null;
|
||||
_shortPlayInfo = json['shortPlayInfo'] != null
|
||||
? ShortPlayInfo.fromJson(json['shortPlayInfo'])
|
||||
: null;
|
||||
if (json['episodeList'] != null) {
|
||||
_episodeList = [];
|
||||
json['episodeList'].forEach((v) {
|
||||
_episodeList?.add(Episode.fromJson(v));
|
||||
});
|
||||
}
|
||||
_checkPoint = json['check_point'] != null
|
||||
? json['check_point'].cast<int>()
|
||||
: [];
|
||||
_isCollect = json['is_collect'];
|
||||
_showShareCoin = json['show_share_coin'];
|
||||
_shareCoin = json['share_coin'];
|
||||
_installCoins = json['install_coins'];
|
||||
_revolution = json['revolution'];
|
||||
_userLevel = json['user_level'];
|
||||
_unlockVideoAdCount = json['unlock_video_ad_count'];
|
||||
_discount = json['discount'];
|
||||
}
|
||||
|
||||
int? _payMode;
|
||||
String? _businessModel;
|
||||
VideoInfo? _videoInfo;
|
||||
ShortPlayInfo? _shortPlayInfo;
|
||||
List<Episode>? _episodeList;
|
||||
List<int>? _checkPoint;
|
||||
bool? _isCollect;
|
||||
bool? _showShareCoin;
|
||||
num? _shareCoin;
|
||||
num? _installCoins;
|
||||
dynamic _revolution;
|
||||
String? _userLevel;
|
||||
num? _unlockVideoAdCount;
|
||||
num? _discount;
|
||||
|
||||
VideoDetailBean copyWith({
|
||||
int? payMode,
|
||||
String? businessModel,
|
||||
VideoInfo? videoInfo,
|
||||
ShortPlayInfo? shortPlayInfo,
|
||||
List<Episode>? episodeList,
|
||||
List<int>? checkPoint,
|
||||
bool? isCollect,
|
||||
bool? showShareCoin,
|
||||
num? shareCoin,
|
||||
num? installCoins,
|
||||
dynamic revolution,
|
||||
String? userLevel,
|
||||
num? unlockVideoAdCount,
|
||||
num? discount,
|
||||
}) => VideoDetailBean(
|
||||
payMode: payMode ?? _payMode,
|
||||
businessModel: businessModel ?? _businessModel,
|
||||
videoInfo: videoInfo ?? _videoInfo,
|
||||
shortPlayInfo: shortPlayInfo ?? _shortPlayInfo,
|
||||
episodeList: episodeList ?? _episodeList,
|
||||
checkPoint: checkPoint ?? _checkPoint,
|
||||
isCollect: isCollect ?? _isCollect,
|
||||
showShareCoin: showShareCoin ?? _showShareCoin,
|
||||
shareCoin: shareCoin ?? _shareCoin,
|
||||
installCoins: installCoins ?? _installCoins,
|
||||
revolution: revolution ?? _revolution,
|
||||
userLevel: userLevel ?? _userLevel,
|
||||
unlockVideoAdCount: unlockVideoAdCount ?? _unlockVideoAdCount,
|
||||
discount: discount ?? _discount,
|
||||
);
|
||||
|
||||
int? get payMode => _payMode;
|
||||
|
||||
String? get businessModel => _businessModel;
|
||||
|
||||
VideoInfo? get videoInfo => _videoInfo;
|
||||
|
||||
ShortPlayInfo? get shortPlayInfo => _shortPlayInfo;
|
||||
|
||||
List<Episode>? get episodeList => _episodeList;
|
||||
|
||||
List<int>? get checkPoint => _checkPoint;
|
||||
|
||||
bool? get isCollect => _isCollect;
|
||||
|
||||
bool? get showShareCoin => _showShareCoin;
|
||||
|
||||
num? get shareCoin => _shareCoin;
|
||||
|
||||
num? get installCoins => _installCoins;
|
||||
|
||||
dynamic get revolution => _revolution;
|
||||
|
||||
String? get userLevel => _userLevel;
|
||||
|
||||
num? get unlockVideoAdCount => _unlockVideoAdCount;
|
||||
|
||||
num? get discount => _discount;
|
||||
|
||||
// VideoDetailBean setters
|
||||
set payMode(int? value) => _payMode = value;
|
||||
|
||||
set businessModel(String? value) => _businessModel = value;
|
||||
|
||||
set videoInfo(VideoInfo? value) => _videoInfo = value;
|
||||
|
||||
set shortPlayInfo(ShortPlayInfo? value) => _shortPlayInfo = value;
|
||||
|
||||
set episodeList(List<Episode>? value) => _episodeList = value;
|
||||
|
||||
set checkPoint(List<int>? value) => _checkPoint = value;
|
||||
|
||||
set isCollect(bool? value) => _isCollect = value;
|
||||
|
||||
set showShareCoin(bool? value) => _showShareCoin = value;
|
||||
|
||||
set shareCoin(num? value) => _shareCoin = value;
|
||||
|
||||
set installCoins(num? value) => _installCoins = value;
|
||||
|
||||
set revolution(dynamic value) => _revolution = value;
|
||||
|
||||
set userLevel(String? value) => _userLevel = value;
|
||||
|
||||
set unlockVideoAdCount(num? value) => _unlockVideoAdCount = value;
|
||||
|
||||
set discount(num? value) => _discount = value;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['pay_mode'] = _payMode;
|
||||
map['business_model'] = _businessModel;
|
||||
if (_videoInfo != null) {
|
||||
map['video_info'] = _videoInfo?.toJson();
|
||||
}
|
||||
if (_shortPlayInfo != null) {
|
||||
map['shortPlayInfo'] = _shortPlayInfo?.toJson();
|
||||
}
|
||||
if (_episodeList != null) {
|
||||
map['episodeList'] = _episodeList?.map((v) => v.toJson()).toList();
|
||||
}
|
||||
map['check_point'] = _checkPoint;
|
||||
map['is_collect'] = _isCollect;
|
||||
map['show_share_coin'] = _showShareCoin;
|
||||
map['share_coin'] = _shareCoin;
|
||||
map['install_coins'] = _installCoins;
|
||||
map['revolution'] = _revolution;
|
||||
map['user_level'] = _userLevel;
|
||||
map['unlock_video_ad_count'] = _unlockVideoAdCount;
|
||||
map['discount'] = _discount;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/// id : 7132
|
||||
/// short_play_video_id : 7132
|
||||
/// short_play_id : 162
|
||||
/// short_id : 11
|
||||
/// promise_view_ad : 0
|
||||
/// video_url : "https://static.wanmwl.com/videom3u8/3f9afda335fd2616d901/3f9afda335fd2616d901.m3u8"
|
||||
/// coins : 0
|
||||
/// vip_coins : 0
|
||||
/// episode : 1
|
||||
/// is_vip : 2
|
||||
/// is_lock : false
|
||||
/// play_seconds : "0"
|
||||
|
||||
Episode episodeListFromJson(String str) => Episode.fromJson(json.decode(str));
|
||||
|
||||
String episodeListToJson(Episode data) => json.encode(data.toJson());
|
||||
|
||||
class Episode {
|
||||
Episode({
|
||||
num? id,
|
||||
num? shortPlayVideoId,
|
||||
num? shortPlayId,
|
||||
num? shortId,
|
||||
num? promiseViewAd,
|
||||
String? videoUrl,
|
||||
num? coins,
|
||||
num? vipCoins,
|
||||
int? episode,
|
||||
num? isVip,
|
||||
bool? isLock,
|
||||
String? playSeconds,
|
||||
}) {
|
||||
_id = id;
|
||||
_shortPlayVideoId = shortPlayVideoId;
|
||||
_shortPlayId = shortPlayId;
|
||||
_shortId = shortId;
|
||||
_promiseViewAd = promiseViewAd;
|
||||
_videoUrl = videoUrl;
|
||||
_coins = coins;
|
||||
_vipCoins = vipCoins;
|
||||
_episode = episode;
|
||||
_isVip = isVip;
|
||||
_isLock = isLock;
|
||||
_playSeconds = playSeconds;
|
||||
}
|
||||
|
||||
Episode.fromJson(dynamic json) {
|
||||
_id = json['id'];
|
||||
_shortPlayVideoId = json['short_play_video_id'];
|
||||
_shortPlayId = json['short_play_id'];
|
||||
_shortId = json['short_id'];
|
||||
_promiseViewAd = json['promise_view_ad'];
|
||||
_videoUrl = json['video_url'];
|
||||
_coins = json['coins'];
|
||||
_vipCoins = json['vip_coins'];
|
||||
_episode = json['episode'];
|
||||
_isVip = json['is_vip'];
|
||||
_isLock = json['is_lock'];
|
||||
_playSeconds = json['play_seconds'];
|
||||
}
|
||||
|
||||
num? _id;
|
||||
num? _shortPlayVideoId;
|
||||
num? _shortPlayId;
|
||||
num? _shortId;
|
||||
num? _promiseViewAd;
|
||||
String? _videoUrl;
|
||||
num? _coins;
|
||||
num? _vipCoins;
|
||||
int? _episode;
|
||||
num? _isVip;
|
||||
bool? _isLock;
|
||||
String? _playSeconds;
|
||||
|
||||
Episode copyWith({
|
||||
num? id,
|
||||
num? shortPlayVideoId,
|
||||
num? shortPlayId,
|
||||
num? shortId,
|
||||
num? promiseViewAd,
|
||||
String? videoUrl,
|
||||
num? coins,
|
||||
num? vipCoins,
|
||||
int? episode,
|
||||
num? isVip,
|
||||
bool? isLock,
|
||||
String? playSeconds,
|
||||
}) => Episode(
|
||||
id: id ?? _id,
|
||||
shortPlayVideoId: shortPlayVideoId ?? _shortPlayVideoId,
|
||||
shortPlayId: shortPlayId ?? _shortPlayId,
|
||||
shortId: shortId ?? _shortId,
|
||||
promiseViewAd: promiseViewAd ?? _promiseViewAd,
|
||||
videoUrl: videoUrl ?? _videoUrl,
|
||||
coins: coins ?? _coins,
|
||||
vipCoins: vipCoins ?? _vipCoins,
|
||||
episode: episode ?? _episode,
|
||||
isVip: isVip ?? _isVip,
|
||||
isLock: isLock ?? _isLock,
|
||||
playSeconds: playSeconds ?? _playSeconds,
|
||||
);
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
num? get shortPlayVideoId => _shortPlayVideoId;
|
||||
|
||||
num? get shortPlayId => _shortPlayId;
|
||||
|
||||
num? get shortId => _shortId;
|
||||
|
||||
num? get promiseViewAd => _promiseViewAd;
|
||||
|
||||
String? get videoUrl => _videoUrl;
|
||||
|
||||
num? get coins => _coins;
|
||||
|
||||
num? get vipCoins => _vipCoins;
|
||||
|
||||
int? get episode => _episode;
|
||||
|
||||
num? get isVip => _isVip;
|
||||
|
||||
bool? get isLock => _isLock;
|
||||
|
||||
String? get playSeconds => _playSeconds;
|
||||
|
||||
// Episode setters
|
||||
set id(num? value) => _id = value;
|
||||
|
||||
set shortPlayVideoId(num? value) => _shortPlayVideoId = value;
|
||||
|
||||
set shortPlayId(num? value) => _shortPlayId = value;
|
||||
|
||||
set shortId(num? value) => _shortId = value;
|
||||
|
||||
set promiseViewAd(num? value) => _promiseViewAd = value;
|
||||
|
||||
set videoUrl(String? value) => _videoUrl = value;
|
||||
|
||||
set coins(num? value) => _coins = value;
|
||||
|
||||
set vipCoins(num? value) => _vipCoins = value;
|
||||
|
||||
set episode(int? value) => _episode = value;
|
||||
|
||||
set isVip(num? value) => _isVip = value;
|
||||
|
||||
set isLock(bool? value) => _isLock = value;
|
||||
|
||||
set playSeconds(String? value) => _playSeconds = value;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = _id;
|
||||
map['short_play_video_id'] = _shortPlayVideoId;
|
||||
map['short_play_id'] = _shortPlayId;
|
||||
map['short_id'] = _shortId;
|
||||
map['promise_view_ad'] = _promiseViewAd;
|
||||
map['video_url'] = _videoUrl;
|
||||
map['coins'] = _coins;
|
||||
map['vip_coins'] = _vipCoins;
|
||||
map['episode'] = _episode;
|
||||
map['is_vip'] = _isVip;
|
||||
map['is_lock'] = _isLock;
|
||||
map['play_seconds'] = _playSeconds;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/// id : 162
|
||||
/// short_id : 11
|
||||
/// short_play_id : 162
|
||||
/// name : "Madam President, Holding Multiple Positions "
|
||||
/// description : "It is rumored outside that the prestigious president of Xu's Group is about to marry the daughter of the Xu family. Just because the name of the Xu family's daughter is Pearl, which is the same as his childhood sweetheart in "
|
||||
/// process : 2
|
||||
/// image_url : "https://static.wanmwl.com/eyJrZXkiOiJpbWFnZS80ZmRhZDg4OWIxNmM3MmQ2MjkwZC5qcGciLCJlZGl0cyI6eyJyZXNpemUiOnsiZml0IjoiY292ZXIifX19?sign=$23f3feca08db05081465b3677a6f671819b85d9f18ad94cc7236f3c89aada01ebe2de98adbca00c6f0c8c5b8621c5e4f6064afb617"
|
||||
/// horizontally_img : "https://static.wanmwl.com/image/ac2f87aad3384032215c.jpg"
|
||||
/// buy_type : 1
|
||||
/// tag_type : ""
|
||||
/// all_coins : 0
|
||||
/// collect_total : 4
|
||||
/// watch_total : 307
|
||||
/// episode_total : 18
|
||||
/// search_click_total : 0
|
||||
/// is_collect : true
|
||||
/// can_share_get_coin : true
|
||||
/// category : ["Sweet Romance "]
|
||||
|
||||
ShortPlayInfo shortPlayInfoFromJson(String str) =>
|
||||
ShortPlayInfo.fromJson(json.decode(str));
|
||||
|
||||
String shortPlayInfoToJson(ShortPlayInfo data) => json.encode(data.toJson());
|
||||
|
||||
class ShortPlayInfo {
|
||||
ShortPlayInfo({
|
||||
num? id,
|
||||
num? shortId,
|
||||
num? shortPlayId,
|
||||
String? name,
|
||||
String? description,
|
||||
num? process,
|
||||
String? imageUrl,
|
||||
String? horizontallyImg,
|
||||
num? buyType,
|
||||
String? tagType,
|
||||
num? allCoins,
|
||||
num? collectTotal,
|
||||
num? watchTotal,
|
||||
num? episodeTotal,
|
||||
num? searchClickTotal,
|
||||
bool? isCollect,
|
||||
bool? canShareGetCoin,
|
||||
List<String>? category,
|
||||
}) {
|
||||
_id = id;
|
||||
_shortId = shortId;
|
||||
_shortPlayId = shortPlayId;
|
||||
_name = name;
|
||||
_description = description;
|
||||
_process = process;
|
||||
_imageUrl = imageUrl;
|
||||
_horizontallyImg = horizontallyImg;
|
||||
_buyType = buyType;
|
||||
_tagType = tagType;
|
||||
_allCoins = allCoins;
|
||||
_collectTotal = collectTotal;
|
||||
_watchTotal = watchTotal;
|
||||
_episodeTotal = episodeTotal;
|
||||
_searchClickTotal = searchClickTotal;
|
||||
_isCollect = isCollect;
|
||||
_canShareGetCoin = canShareGetCoin;
|
||||
_category = category;
|
||||
}
|
||||
|
||||
ShortPlayInfo.fromJson(dynamic json) {
|
||||
_id = json['id'];
|
||||
_shortId = json['short_id'];
|
||||
_shortPlayId = json['short_play_id'];
|
||||
_name = json['name'];
|
||||
_description = json['description'];
|
||||
_process = json['process'];
|
||||
_imageUrl = json['image_url'];
|
||||
_horizontallyImg = json['horizontally_img'];
|
||||
_buyType = json['buy_type'];
|
||||
_tagType = json['tag_type'];
|
||||
_allCoins = json['all_coins'];
|
||||
_collectTotal = json['collect_total'];
|
||||
_watchTotal = json['watch_total'];
|
||||
_episodeTotal = json['episode_total'];
|
||||
_searchClickTotal = json['search_click_total'];
|
||||
_isCollect = json['is_collect'];
|
||||
_canShareGetCoin = json['can_share_get_coin'];
|
||||
_category = json['category'] != null ? json['category'].cast<String>() : [];
|
||||
}
|
||||
|
||||
num? _id;
|
||||
num? _shortId;
|
||||
num? _shortPlayId;
|
||||
String? _name;
|
||||
String? _description;
|
||||
num? _process;
|
||||
String? _imageUrl;
|
||||
String? _horizontallyImg;
|
||||
num? _buyType;
|
||||
String? _tagType;
|
||||
num? _allCoins;
|
||||
num? _collectTotal;
|
||||
num? _watchTotal;
|
||||
num? _episodeTotal;
|
||||
num? _searchClickTotal;
|
||||
bool? _isCollect;
|
||||
bool? _canShareGetCoin;
|
||||
List<String>? _category;
|
||||
|
||||
ShortPlayInfo copyWith({
|
||||
num? id,
|
||||
num? shortId,
|
||||
num? shortPlayId,
|
||||
String? name,
|
||||
String? description,
|
||||
num? process,
|
||||
String? imageUrl,
|
||||
String? horizontallyImg,
|
||||
num? buyType,
|
||||
String? tagType,
|
||||
num? allCoins,
|
||||
num? collectTotal,
|
||||
num? watchTotal,
|
||||
num? episodeTotal,
|
||||
num? searchClickTotal,
|
||||
bool? isCollect,
|
||||
bool? canShareGetCoin,
|
||||
List<String>? category,
|
||||
}) => ShortPlayInfo(
|
||||
id: id ?? _id,
|
||||
shortId: shortId ?? _shortId,
|
||||
shortPlayId: shortPlayId ?? _shortPlayId,
|
||||
name: name ?? _name,
|
||||
description: description ?? _description,
|
||||
process: process ?? _process,
|
||||
imageUrl: imageUrl ?? _imageUrl,
|
||||
horizontallyImg: horizontallyImg ?? _horizontallyImg,
|
||||
buyType: buyType ?? _buyType,
|
||||
tagType: tagType ?? _tagType,
|
||||
allCoins: allCoins ?? _allCoins,
|
||||
collectTotal: collectTotal ?? _collectTotal,
|
||||
watchTotal: watchTotal ?? _watchTotal,
|
||||
episodeTotal: episodeTotal ?? _episodeTotal,
|
||||
searchClickTotal: searchClickTotal ?? _searchClickTotal,
|
||||
isCollect: isCollect ?? _isCollect,
|
||||
canShareGetCoin: canShareGetCoin ?? _canShareGetCoin,
|
||||
category: category ?? _category,
|
||||
);
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
num? get shortId => _shortId;
|
||||
|
||||
num? get shortPlayId => _shortPlayId;
|
||||
|
||||
String? get name => _name;
|
||||
|
||||
String? get description => _description;
|
||||
|
||||
num? get process => _process;
|
||||
|
||||
String? get imageUrl => _imageUrl;
|
||||
|
||||
String? get horizontallyImg => _horizontallyImg;
|
||||
|
||||
num? get buyType => _buyType;
|
||||
|
||||
String? get tagType => _tagType;
|
||||
|
||||
num? get allCoins => _allCoins;
|
||||
|
||||
num? get collectTotal => _collectTotal;
|
||||
|
||||
num? get watchTotal => _watchTotal;
|
||||
|
||||
num? get episodeTotal => _episodeTotal;
|
||||
|
||||
num? get searchClickTotal => _searchClickTotal;
|
||||
|
||||
bool? get isCollect => _isCollect;
|
||||
|
||||
bool? get canShareGetCoin => _canShareGetCoin;
|
||||
|
||||
List<String>? get category => _category;
|
||||
|
||||
// ShortPlayInfo setters
|
||||
set id(num? value) => _id = value;
|
||||
|
||||
set shortId(num? value) => _shortId = value;
|
||||
|
||||
set shortPlayId(num? value) => _shortPlayId = value;
|
||||
|
||||
set name(String? value) => _name = value;
|
||||
|
||||
set description(String? value) => _description = value;
|
||||
|
||||
set process(num? value) => _process = value;
|
||||
|
||||
set imageUrl(String? value) => _imageUrl = value;
|
||||
|
||||
set horizontallyImg(String? value) => _horizontallyImg = value;
|
||||
|
||||
set buyType(num? value) => _buyType = value;
|
||||
|
||||
set tagType(String? value) => _tagType = value;
|
||||
|
||||
set allCoins(num? value) => _allCoins = value;
|
||||
|
||||
set collectTotal(num? value) => _collectTotal = value;
|
||||
|
||||
set watchTotal(num? value) => _watchTotal = value;
|
||||
|
||||
set episodeTotal(num? value) => _episodeTotal = value;
|
||||
|
||||
set searchClickTotal(num? value) => _searchClickTotal = value;
|
||||
|
||||
set isCollect(bool? value) => _isCollect = value;
|
||||
|
||||
set canShareGetCoin(bool? value) => _canShareGetCoin = value;
|
||||
|
||||
set category(List<String>? value) => _category = value;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = _id;
|
||||
map['short_id'] = _shortId;
|
||||
map['short_play_id'] = _shortPlayId;
|
||||
map['name'] = _name;
|
||||
map['description'] = _description;
|
||||
map['process'] = _process;
|
||||
map['image_url'] = _imageUrl;
|
||||
map['horizontally_img'] = _horizontallyImg;
|
||||
map['buy_type'] = _buyType;
|
||||
map['tag_type'] = _tagType;
|
||||
map['all_coins'] = _allCoins;
|
||||
map['collect_total'] = _collectTotal;
|
||||
map['watch_total'] = _watchTotal;
|
||||
map['episode_total'] = _episodeTotal;
|
||||
map['search_click_total'] = _searchClickTotal;
|
||||
map['is_collect'] = _isCollect;
|
||||
map['can_share_get_coin'] = _canShareGetCoin;
|
||||
map['category'] = _category;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/// id : 7132
|
||||
/// short_play_video_id : 7132
|
||||
/// short_play_id : 162
|
||||
/// short_id : 11
|
||||
/// promise_view_ad : 0
|
||||
/// video_url : "https://static.wanmwl.com/videom3u8/3f9afda335fd2616d901/3f9afda335fd2616d901.m3u8"
|
||||
/// coins : 0
|
||||
/// vip_coins : 0
|
||||
/// episode : 1
|
||||
/// is_vip : 2
|
||||
/// play_seconds : "0"
|
||||
|
||||
VideoInfo videoInfoFromJson(String str) => VideoInfo.fromJson(json.decode(str));
|
||||
|
||||
String videoInfoToJson(VideoInfo data) => json.encode(data.toJson());
|
||||
|
||||
class VideoInfo {
|
||||
VideoInfo({
|
||||
num? id,
|
||||
num? shortPlayVideoId,
|
||||
num? shortPlayId,
|
||||
num? shortId,
|
||||
num? promiseViewAd,
|
||||
String? videoUrl,
|
||||
num? coins,
|
||||
num? vipCoins,
|
||||
int? episode,
|
||||
num? isVip,
|
||||
String? playSeconds,
|
||||
}) {
|
||||
_id = id;
|
||||
_shortPlayVideoId = shortPlayVideoId;
|
||||
_shortPlayId = shortPlayId;
|
||||
_shortId = shortId;
|
||||
_promiseViewAd = promiseViewAd;
|
||||
_videoUrl = videoUrl;
|
||||
_coins = coins;
|
||||
_vipCoins = vipCoins;
|
||||
_episode = episode;
|
||||
_isVip = isVip;
|
||||
_playSeconds = playSeconds;
|
||||
}
|
||||
|
||||
VideoInfo.fromJson(dynamic json) {
|
||||
_id = json['id'];
|
||||
_shortPlayVideoId = json['short_play_video_id'];
|
||||
_shortPlayId = json['short_play_id'];
|
||||
_shortId = json['short_id'];
|
||||
_promiseViewAd = json['promise_view_ad'];
|
||||
_videoUrl = json['video_url'];
|
||||
_coins = json['coins'];
|
||||
_vipCoins = json['vip_coins'];
|
||||
_episode = json['episode'];
|
||||
_isVip = json['is_vip'];
|
||||
_playSeconds = json['play_seconds'];
|
||||
}
|
||||
|
||||
num? _id;
|
||||
num? _shortPlayVideoId;
|
||||
num? _shortPlayId;
|
||||
num? _shortId;
|
||||
num? _promiseViewAd;
|
||||
String? _videoUrl;
|
||||
num? _coins;
|
||||
num? _vipCoins;
|
||||
int? _episode;
|
||||
num? _isVip;
|
||||
String? _playSeconds;
|
||||
|
||||
VideoInfo copyWith({
|
||||
num? id,
|
||||
num? shortPlayVideoId,
|
||||
num? shortPlayId,
|
||||
num? shortId,
|
||||
num? promiseViewAd,
|
||||
String? videoUrl,
|
||||
num? coins,
|
||||
num? vipCoins,
|
||||
int? episode,
|
||||
num? isVip,
|
||||
String? playSeconds,
|
||||
}) => VideoInfo(
|
||||
id: id ?? _id,
|
||||
shortPlayVideoId: shortPlayVideoId ?? _shortPlayVideoId,
|
||||
shortPlayId: shortPlayId ?? _shortPlayId,
|
||||
shortId: shortId ?? _shortId,
|
||||
promiseViewAd: promiseViewAd ?? _promiseViewAd,
|
||||
videoUrl: videoUrl ?? _videoUrl,
|
||||
coins: coins ?? _coins,
|
||||
vipCoins: vipCoins ?? _vipCoins,
|
||||
episode: episode ?? _episode,
|
||||
isVip: isVip ?? _isVip,
|
||||
playSeconds: playSeconds ?? _playSeconds,
|
||||
);
|
||||
|
||||
num? get id => _id;
|
||||
|
||||
num? get shortPlayVideoId => _shortPlayVideoId;
|
||||
|
||||
num? get shortPlayId => _shortPlayId;
|
||||
|
||||
num? get shortId => _shortId;
|
||||
|
||||
num? get promiseViewAd => _promiseViewAd;
|
||||
|
||||
String? get videoUrl => _videoUrl;
|
||||
|
||||
num? get coins => _coins;
|
||||
|
||||
num? get vipCoins => _vipCoins;
|
||||
|
||||
int? get episode => _episode;
|
||||
|
||||
num? get isVip => _isVip;
|
||||
|
||||
String? get playSeconds => _playSeconds;
|
||||
|
||||
// VideoInfo setters
|
||||
set id(num? value) => _id = value;
|
||||
|
||||
set shortPlayVideoId(num? value) => _shortPlayVideoId = value;
|
||||
|
||||
set shortPlayId(num? value) => _shortPlayId = value;
|
||||
|
||||
set shortId(num? value) => _shortId = value;
|
||||
|
||||
set promiseViewAd(num? value) => _promiseViewAd = value;
|
||||
|
||||
set videoUrl(String? value) => _videoUrl = value;
|
||||
|
||||
set coins(num? value) => _coins = value;
|
||||
|
||||
set vipCoins(num? value) => _vipCoins = value;
|
||||
|
||||
set episode(int? value) => _episode = value;
|
||||
|
||||
set isVip(num? value) => _isVip = value;
|
||||
|
||||
set playSeconds(String? value) => _playSeconds = value;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final map = <String, dynamic>{};
|
||||
map['id'] = _id;
|
||||
map['short_play_video_id'] = _shortPlayVideoId;
|
||||
map['short_play_id'] = _shortPlayId;
|
||||
map['short_id'] = _shortId;
|
||||
map['promise_view_ad'] = _promiseViewAd;
|
||||
map['video_url'] = _videoUrl;
|
||||
map['coins'] = _coins;
|
||||
map['vip_coins'] = _vipCoins;
|
||||
map['episode'] = _episode;
|
||||
map['is_vip'] = _isVip;
|
||||
map['play_seconds'] = _playSeconds;
|
||||
return map;
|
||||
}
|
||||
}
|
240
lib/kt_pages/kt_explore/logic.dart
Normal file
@ -0,0 +1,240 @@
|
||||
import 'package:flutter_kinetra/kt_pages/kt_explore/state.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
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';
|
||||
|
||||
class KtExploreLogic extends GetxController {
|
||||
final state = KtExploreState();
|
||||
final EasyRefreshController refreshCtrl = EasyRefreshController(
|
||||
controlFinishLoad: true,
|
||||
);
|
||||
final PageController pageController = PageController(viewportFraction: .95);
|
||||
final int preloadRange = 1;
|
||||
bool isRefresh = false;
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
fetchDiscoverList();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
super.onClose();
|
||||
clearController();
|
||||
refreshCtrl.dispose();
|
||||
pageController.dispose();
|
||||
}
|
||||
|
||||
clearController() {
|
||||
for (var item in state.controllers) {
|
||||
item?.pause();
|
||||
item?.dispose();
|
||||
}
|
||||
state.controllers.clear();
|
||||
}
|
||||
|
||||
fetchDiscoverList({bool refresh = false}) async {
|
||||
state.loadStatus = KtLoadStatusType.loading;
|
||||
|
||||
if (refresh) {
|
||||
isRefresh = true;
|
||||
state.curIndex = 1;
|
||||
state.videoList.clear();
|
||||
clearController();
|
||||
}
|
||||
|
||||
if (refresh) refreshCtrl.finishLoad();
|
||||
try {
|
||||
ApiResponse res = await KtHttpClient().request(
|
||||
KtApis.getRecommands,
|
||||
queryParameters: {'current_page': state.curIndex, 'page_size': 20},
|
||||
method: HttpMethod.get,
|
||||
);
|
||||
if (refresh) {
|
||||
isRefresh = false;
|
||||
update();
|
||||
}
|
||||
|
||||
if (res.success) {
|
||||
state.loadStatus = KtLoadStatusType.loadSuccess;
|
||||
List<KtShortVideoBean> list = [
|
||||
...res.data['list'].map((item) => KtShortVideoBean.fromJson(item)),
|
||||
];
|
||||
state.videoList.addAll(list);
|
||||
// if (videoId == -1) {
|
||||
// video = state.videoList.first;
|
||||
// videoId = state.videoList.first.id ?? -1;
|
||||
// }
|
||||
state.controllers = List<VideoPlayerController?>.filled(
|
||||
state.videoList.length,
|
||||
null,
|
||||
growable: true,
|
||||
);
|
||||
|
||||
if (state.videoList.isNotEmpty && state.curIndex == 1) {
|
||||
state.currentPage = 0;
|
||||
_initializeController(0);
|
||||
}
|
||||
update();
|
||||
} else {
|
||||
if (refresh) refreshCtrl.finishRefresh(IndicatorResult.fail);
|
||||
state.loadStatus = KtLoadStatusType.loadFailed;
|
||||
}
|
||||
} catch (e) {
|
||||
if (refresh) {
|
||||
isRefresh = false;
|
||||
update();
|
||||
}
|
||||
state.loadStatus = KtLoadStatusType.loadFailed;
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
// 切换剧集时处理视频状态
|
||||
Future<void> onPageChanged(int index, {bool isToggle = false}) async {
|
||||
if (index < 0 || index >= state.videoList.length) return;
|
||||
|
||||
// 暂停当前视频
|
||||
if (state.controllers[state.currentPage]?.value.isPlaying ?? false) {
|
||||
await state.controllers[state.currentPage]?.pause();
|
||||
}
|
||||
|
||||
state.currentPage = index;
|
||||
if (isToggle) {
|
||||
// loadStatusType = LoadStatusType.loading;
|
||||
// update();
|
||||
|
||||
await _initializeController(index);
|
||||
// loadStatusType = LoadStatusType.loadSuccess;
|
||||
update();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
pageController.jumpToPage(index);
|
||||
});
|
||||
}
|
||||
if (state.controllers[index] != null) {
|
||||
state.controllers[index]?.play();
|
||||
} else {
|
||||
if (!isToggle) await _initializeController(index);
|
||||
state.controllers[index]?.play();
|
||||
}
|
||||
// 预加载新的相邻视频,并释放多余控制器
|
||||
_preloadAdjacentVideos();
|
||||
update();
|
||||
// });
|
||||
}
|
||||
|
||||
// 释放非当前、前后的视频控制器,减少内存占用
|
||||
void _releaseUnusedControllers() {
|
||||
for (int i = 0; i < state.controllers.length; i++) {
|
||||
if (i < state.currentPage - 1 || i > state.currentPage + 1) {
|
||||
state.controllers[i]?.dispose();
|
||||
state.controllers[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化视频控制器
|
||||
Future<void> _initializeController(int index) async {
|
||||
if (index < 0 || index >= state.videoList.length) return;
|
||||
if (state.controllers[index] != null) return;
|
||||
final episode = state.videoList[index];
|
||||
final controller = VideoPlayerController.networkUrl(
|
||||
Uri.parse(episode.videoInfo!.videoUrl!),
|
||||
formatHint: VideoFormat.hls,
|
||||
);
|
||||
|
||||
state.controllers[index] = controller;
|
||||
|
||||
try {
|
||||
await controller.initialize();
|
||||
if (index == state.currentPage) {
|
||||
controller.play();
|
||||
update();
|
||||
}
|
||||
controller.addListener(() {
|
||||
if (state.currentPage == index) update();
|
||||
|
||||
if (controller.value.isCompleted && !controller.value.isBuffering) {
|
||||
onPageChanged(index + 1, isToggle: true);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// 可根据需要处理异常
|
||||
// UserUtil().reportErrorEvent(
|
||||
// 'video initialize failed',
|
||||
// UserUtil.videoError,
|
||||
// errMsg: e.toString(),
|
||||
// payData: episode.toJson(),
|
||||
// shortPlayId: episode.shortPlayId ?? 0,
|
||||
// shortPlayVideoId: episode.shortPlayVideoId ?? 0,
|
||||
// );
|
||||
debugPrint('---err:$e');
|
||||
}
|
||||
}
|
||||
|
||||
// 预加载相邻视频
|
||||
void _preloadAdjacentVideos() {
|
||||
if (state.currentPage > 0) _initializeController(state.currentPage - 1);
|
||||
if (state.currentPage < state.videoList.length - 1) {
|
||||
_initializeController(state.currentPage + 1);
|
||||
}
|
||||
_releaseUnusedControllers();
|
||||
}
|
||||
|
||||
likeVideo(KtShortVideoBean video) {
|
||||
if (video.isCollect ?? false) {
|
||||
video.collectTotal = video.collectTotal! - 1;
|
||||
if (video.collectTotal! < 0) {
|
||||
video.collectTotal = 0;
|
||||
}
|
||||
KtHttpClient().request(
|
||||
KtApis.deleteFavoriteVideo,
|
||||
data: {
|
||||
"short_play_id": video.shortPlayId,
|
||||
"video_id": video.videoInfo!.id,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
video.collectTotal = video.collectTotal! + 1;
|
||||
KtHttpClient().request(
|
||||
KtApis.collectVideo,
|
||||
data: {
|
||||
"short_play_id": video.shortPlayId,
|
||||
"video_id": video.videoInfo!.id,
|
||||
},
|
||||
);
|
||||
}
|
||||
video.isCollect = !(video.isCollect ?? false);
|
||||
update();
|
||||
}
|
||||
|
||||
KtShortVideoBean get curVideo => state.videoList[state.currentPage];
|
||||
|
||||
setCollectVideo(int shortPlayId, {bool isCollect = true}) {
|
||||
KtShortVideoBean? video = state.videoList.firstWhereOrNull(
|
||||
(item) => item.shortPlayId == shortPlayId,
|
||||
);
|
||||
if (video == null) return;
|
||||
video.isCollect = isCollect;
|
||||
video.collectTotal = video.collectTotal! + (isCollect ? 1 : -1);
|
||||
update();
|
||||
}
|
||||
|
||||
//
|
||||
// Future<void> onRefresh() async {
|
||||
// state.currentPage = 0;
|
||||
// state.curIndex = 1;
|
||||
// // clearController();
|
||||
// // update();
|
||||
// WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||
// await fetchDiscoverList(refresh: true);
|
||||
// });
|
||||
// }
|
||||
}
|
13
lib/kt_pages/kt_explore/state.dart
Normal file
@ -0,0 +1,13 @@
|
||||
import 'package:flutter_kinetra/kt_model/kt_short_video_bean.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
import '../../kt_widgets/kt_status_widget.dart';
|
||||
|
||||
class KtExploreState {
|
||||
int curIndex = 1;
|
||||
List<KtShortVideoBean> videoList = [];
|
||||
List<VideoPlayerController?> controllers = [];
|
||||
|
||||
int currentPage = 0;
|
||||
KtLoadStatusType loadStatus = KtLoadStatusType.loading;
|
||||
}
|
@ -1,4 +1,16 @@
|
||||
import 'package:easy_refresh/easy_refresh.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_kinetra/kt_utils/kt_string_extend.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_short_video_bean.dart';
|
||||
import '../../kt_widgets/kt_network_image.dart';
|
||||
import '../../kt_widgets/kt_status_widget.dart';
|
||||
import '../kt_routes.dart';
|
||||
import 'logic.dart';
|
||||
|
||||
class KtExplorePage extends StatefulWidget {
|
||||
const KtExplorePage({super.key});
|
||||
@ -7,9 +19,256 @@ class KtExplorePage extends StatefulWidget {
|
||||
State<KtExplorePage> createState() => _KtExplorePageState();
|
||||
}
|
||||
|
||||
class _KtExplorePageState extends State<KtExplorePage> {
|
||||
class _KtExplorePageState extends State<KtExplorePage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
final logic = Get.put(KtExploreLogic());
|
||||
final state = Get.find<KtExploreLogic>().state;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold();
|
||||
super.build(context);
|
||||
return GetBuilder<KtExploreLogic>(
|
||||
builder: (ctrl) {
|
||||
if (state.loadStatus == KtLoadStatusType.loadNoData ||
|
||||
state.loadStatus == KtLoadStatusType.loadFailed) {
|
||||
return KtStatusWidget(
|
||||
type: KtErrorStatusType.nothingYet,
|
||||
onPressed: logic.fetchDiscoverList,
|
||||
);
|
||||
}
|
||||
return Container(
|
||||
width: ScreenUtil().screenWidth,
|
||||
height: ScreenUtil().screenHeight,
|
||||
padding: EdgeInsets.only(
|
||||
top: ScreenUtil().statusBarHeight,
|
||||
left: 18.w,
|
||||
right: 18.w,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('explore_bg.png'.ktIcon),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(height: 40.w),
|
||||
Expanded(
|
||||
child: EasyRefresh(
|
||||
controller: logic.refreshCtrl,
|
||||
header: MaterialHeader(),
|
||||
footer: MaterialFooter(),
|
||||
onRefresh: () => logic.fetchDiscoverList(refresh: true),
|
||||
child: PageView.builder(
|
||||
scrollDirection: Axis.vertical,
|
||||
itemCount: state.videoList.length,
|
||||
controller: logic.pageController,
|
||||
onPageChanged: (index) => logic.onPageChanged(index),
|
||||
itemBuilder: (_, index) =>
|
||||
videoPlayerItem(index: index),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
Image.asset('ic_explore_ip.png'.ktIcon, width: 56.8.w),
|
||||
Positioned(
|
||||
top: 0.w,
|
||||
right: -2.w,
|
||||
child: Image.asset(
|
||||
'ic_explore_tv.png'.ktIcon,
|
||||
width: 19.w,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 12.w),
|
||||
Text(
|
||||
"Unlock Your Next Binge\nObsession",
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
color: Colors.black,
|
||||
fontStyle: FontStyle.italic,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget videoPlayerItem({int index = 0}) {
|
||||
return GetBuilder<KtExploreLogic>(
|
||||
builder: (ctrl) {
|
||||
if (state.controllers.isEmpty) return Container();
|
||||
final VideoPlayerController? videoPlayerController =
|
||||
state.controllers[index];
|
||||
if (videoPlayerController == null ||
|
||||
!videoPlayerController.value.isInitialized) {
|
||||
return Stack(
|
||||
children: [
|
||||
KtNetworkImage(
|
||||
imageUrl: state.videoList[index].imageUrl!,
|
||||
width: ScreenUtil().screenWidth - 36.w,
|
||||
height: 540.w,
|
||||
fit: BoxFit.cover,
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
),
|
||||
Center(child: CircularProgressIndicator()),
|
||||
],
|
||||
);
|
||||
}
|
||||
KtShortVideoBean item = state.videoList[index];
|
||||
return Container(
|
||||
margin: EdgeInsets.only(bottom: 18.w),
|
||||
alignment: Alignment.topCenter,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
videoPlayerController.value.isPlaying
|
||||
? videoPlayerController.pause()
|
||||
: videoPlayerController.play();
|
||||
setState(() {});
|
||||
},
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: VisibilityDetector(
|
||||
key: ValueKey(
|
||||
'discover-video-${state.videoList[index].id}',
|
||||
),
|
||||
onVisibilityChanged: (info) {
|
||||
if (info.visibleFraction > 0.85 &&
|
||||
state.currentPage == index &&
|
||||
!videoPlayerController.value.isPlaying) {
|
||||
videoPlayerController.play();
|
||||
logic.update();
|
||||
}
|
||||
|
||||
if (info.visibleFraction < 0.09 &&
|
||||
videoPlayerController.value.isPlaying) {
|
||||
videoPlayerController.pause();
|
||||
logic.update();
|
||||
}
|
||||
},
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(16.w),
|
||||
child: SizedBox(
|
||||
width: ScreenUtil().screenWidth - 36.w,
|
||||
height: 540.w,
|
||||
child: VideoPlayer(videoPlayerController),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Container(
|
||||
width: ScreenUtil().screenWidth - 56.w,
|
||||
margin: EdgeInsets.only(left: 12.w),
|
||||
child: Text(
|
||||
item.name ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 11.w),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
Get.toNamed(
|
||||
KtRoutes.shortVideo,
|
||||
arguments: {
|
||||
'shortPlayId': item.shortPlayId,
|
||||
'imageUrl': item.imageUrl,
|
||||
'isFromDiscover': true,
|
||||
},
|
||||
)?.then((v) {
|
||||
videoPlayerController.play();
|
||||
setState(() {});
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
width: ScreenUtil().screenWidth - 60.w,
|
||||
margin: EdgeInsets.symmetric(horizontal: 12.w),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 12.w,
|
||||
vertical: 9.w,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFF1E1E20).withValues(alpha: .3),
|
||||
borderRadius: BorderRadius.circular(100),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset('ic_ep.png'.ktIcon, width: 16.w),
|
||||
SizedBox(width: 6.w),
|
||||
Text(
|
||||
'Watch the complete series',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Image.asset(
|
||||
'ic_right_white.png'.ktIcon,
|
||||
width: 10.w,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.w),
|
||||
|
||||
// if (state.controllers[state.currentPage] != null)
|
||||
// DrVideoProgressBar(
|
||||
// controller: state.controllers[state.currentPage]!,
|
||||
// width: ScreenUtil().screenWidth,
|
||||
// ),
|
||||
],
|
||||
),
|
||||
|
||||
if (!videoPlayerController.value.isPlaying)
|
||||
Positioned(
|
||||
top: 240.h,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
videoPlayerController.value.isPlaying
|
||||
? videoPlayerController.pause()
|
||||
: videoPlayerController.play();
|
||||
|
||||
setState(() {});
|
||||
},
|
||||
child: Image.asset('ic_play.png'.ktIcon, width: 62.w),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
}
|
||||
|
274
lib/kt_pages/kt_home/kt_search_page.dart
Normal file
@ -0,0 +1,274 @@
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flustars/flustars.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:flutter_kinetra/kt_pages/kt_routes.dart';
|
||||
import 'package:flutter_kinetra/kt_utils/kt_string_extend.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart' hide ScreenUtil;
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../dio_cilent/kt_apis.dart';
|
||||
import '../../../kt_utils/kt_keys.dart';
|
||||
import '../../../kt_utils/kt_utils.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../kt_widgets/kt_status_widget.dart';
|
||||
|
||||
class KtSearchPage extends StatefulWidget {
|
||||
const KtSearchPage({super.key});
|
||||
|
||||
@override
|
||||
SignInActivityPageState createState() => SignInActivityPageState();
|
||||
}
|
||||
|
||||
class SignInActivityPageState extends State<KtSearchPage> with RouteAware {
|
||||
InAppWebViewController? _webViewController;
|
||||
late PullToRefreshController _webRefreshController;
|
||||
late Map<String, String> _userData;
|
||||
KtLoadStatusType loadingStatus = KtLoadStatusType.loading;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initUserData();
|
||||
_initRefreshController();
|
||||
}
|
||||
|
||||
void _initUserData() {
|
||||
_userData = {
|
||||
'time_zone': KtUtils.getTimeZoneOffset(DateTime.now()),
|
||||
'type': Platform.isAndroid ? 'android' : 'ios',
|
||||
'lang': 'en',
|
||||
// 'theme': 'theme_7',
|
||||
'token': SpUtil.getString(KtKeys.token) ?? '',
|
||||
};
|
||||
debugPrint('-----userData:$_userData');
|
||||
}
|
||||
|
||||
_initRefreshController() {
|
||||
_webRefreshController = PullToRefreshController(
|
||||
settings: PullToRefreshSettings(enabled: true),
|
||||
onRefresh: () async {
|
||||
if (Platform.isAndroid) {
|
||||
_webViewController?.reload();
|
||||
} else if (Platform.isIOS) {
|
||||
_webViewController?.loadUrl(
|
||||
urlRequest: URLRequest(url: WebUri(KtApis.WEB_SITE_SEARCH)),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
routeObserver.subscribe(this, ModalRoute.of(context)! as PageRoute);
|
||||
}
|
||||
|
||||
// 从别的路由回来
|
||||
@override
|
||||
void didPopNext() {
|
||||
super.didPopNext();
|
||||
_webViewController?.reload();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_webViewController?.dispose();
|
||||
}
|
||||
|
||||
// 处理传递的消息
|
||||
void _handleWebMessage(String jsonS) async {
|
||||
if (jsonS.isEmpty) return;
|
||||
Map<String, dynamic>? webParams;
|
||||
webParams = jsonDecode(jsonS);
|
||||
if (webParams == null) {
|
||||
debugPrint("没有获取到传递过来的参数");
|
||||
return;
|
||||
}
|
||||
Get.toNamed(
|
||||
KtRoutes.shortVideo,
|
||||
arguments: {'shortPlayId': webParams['short_play_id']},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: ScreenUtil().screenWidth,
|
||||
height: ScreenUtil().screenHeight,
|
||||
padding: EdgeInsets.only(top: ScreenUtil().statusBarHeight + 30.w),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('bg2.png'.ktIcon),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
_buildAppBar(context),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
InAppWebView(
|
||||
pullToRefreshController: _webRefreshController,
|
||||
initialSettings: InAppWebViewSettings(
|
||||
cacheEnabled: false,
|
||||
javaScriptEnabled: true,
|
||||
alwaysBounceVertical: true,
|
||||
allowsBackForwardNavigationGestures: true,
|
||||
domStorageEnabled: false,
|
||||
clearCache: true,
|
||||
transparentBackground: true,
|
||||
useShouldOverrideUrlLoading: false,
|
||||
),
|
||||
// 注入两个handler
|
||||
initialUserScripts: UnmodifiableListView<UserScript>([
|
||||
UserScript(
|
||||
source: """
|
||||
window.AndroidInterface = {
|
||||
getUserInfo: function() {
|
||||
return window.flutter_inappwebview.callHandler('getUserInfo');
|
||||
},
|
||||
js2app: function(jsonS) {
|
||||
return window.flutter_inappwebview.callHandler('js2app',jsonS);
|
||||
}
|
||||
};
|
||||
|
||||
""",
|
||||
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START,
|
||||
),
|
||||
]),
|
||||
onWebViewCreated: (controller) async {
|
||||
_webViewController = controller;
|
||||
_webViewController?.addJavaScriptHandler(
|
||||
handlerName: 'getUserInfo',
|
||||
callback: (_) => jsonEncode(_userData),
|
||||
);
|
||||
await _webViewController?.addWebMessageListener(
|
||||
WebMessageListener(
|
||||
jsObjectName: "openDetail",
|
||||
allowedOriginRules: {"*"},
|
||||
onPostMessage:
|
||||
(message, sourceOrigin, isMainFrame, replyProxy) {
|
||||
print('----callback--jsonS:$message');
|
||||
if (message?.data != null) {
|
||||
_handleWebMessage(message?.data);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
await _webViewController?.loadUrl(
|
||||
urlRequest: URLRequest(
|
||||
url: WebUri(KtApis.WEB_SITE_SEARCH),
|
||||
),
|
||||
);
|
||||
},
|
||||
onLoadStart: (controller, url) {
|
||||
setState(() {
|
||||
loadingStatus = KtLoadStatusType.loading;
|
||||
});
|
||||
},
|
||||
onLoadStop: (controller, url) async {
|
||||
// await _webViewController?.evaluateJavascript(
|
||||
// source: '''
|
||||
// document.body.style.backgroundColor = "transparent"
|
||||
// document.style.backgroundColor = "transparent"
|
||||
// ''',
|
||||
// );
|
||||
if (Platform.isIOS) {
|
||||
String userJsonStr = jsonEncode(_userData);
|
||||
Future.delayed(const Duration(seconds: 1)).then((_) {
|
||||
controller.evaluateJavascript(
|
||||
source:
|
||||
'''
|
||||
if(typeof window.receiveDataFromNative === 'function') {
|
||||
window.receiveDataFromNative($userJsonStr);
|
||||
}
|
||||
''',
|
||||
);
|
||||
});
|
||||
} else if (Platform.isAndroid) {
|
||||
await controller.evaluateJavascript(
|
||||
source: """
|
||||
window.AndroidInterface = {
|
||||
getUserInfo: async function () {
|
||||
return window.flutter_inappwebview.callHandler('getUserInfo');
|
||||
},
|
||||
};
|
||||
""",
|
||||
);
|
||||
}
|
||||
setState(() {
|
||||
loadingStatus = KtLoadStatusType.loadSuccess;
|
||||
});
|
||||
_webRefreshController.endRefreshing();
|
||||
},
|
||||
onReceivedError: (controller, request, error) {
|
||||
_webRefreshController.endRefreshing();
|
||||
Future.delayed(const Duration(milliseconds: 100)).then((_) {
|
||||
setState(() {
|
||||
loadingStatus = KtLoadStatusType.loadFailed;
|
||||
});
|
||||
});
|
||||
},
|
||||
),
|
||||
_buildWidget(),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildAppBar(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Image.asset('ic_back.png'.ktIcon, width: 10.w),
|
||||
onPressed: () => Navigator.of(context).maybePop(),
|
||||
),
|
||||
Text(
|
||||
'Search',
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
Container(width: 24.w),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWidget() {
|
||||
print('----loadStatus:$loadingStatus');
|
||||
switch (loadingStatus) {
|
||||
case KtLoadStatusType.loading:
|
||||
return Center(child: CircularProgressIndicator());
|
||||
case KtLoadStatusType.loadFailed:
|
||||
return KtStatusWidget(
|
||||
type: KtErrorStatusType.noNetwork,
|
||||
onPressed: () {
|
||||
_webViewController?.loadUrl(
|
||||
urlRequest: URLRequest(url: WebUri(KtApis.WEB_SITE_SEARCH)),
|
||||
);
|
||||
},
|
||||
);
|
||||
case KtLoadStatusType.loadNoData:
|
||||
return KtStatusWidget(
|
||||
type: KtErrorStatusType.notFound,
|
||||
onPressed: () {
|
||||
_webViewController?.reload();
|
||||
},
|
||||
);
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import 'package:easy_refresh/easy_refresh.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';
|
||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||
|
||||
import '../../dio_cilent/kt_apis.dart';
|
||||
import '../../dio_cilent/kt_request.dart';
|
||||
@ -10,21 +11,39 @@ import '../../kt_widgets/kt_status_widget.dart';
|
||||
class KtHomeLogic extends GetxController {
|
||||
final state = KtHomeState();
|
||||
|
||||
final EasyRefreshController easyRefreshController = EasyRefreshController(
|
||||
controlFinishRefresh: true,
|
||||
controlFinishLoad: true,
|
||||
);
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
// TODO: implement onReady
|
||||
super.onReady();
|
||||
getHomeInfo();
|
||||
refreshData();
|
||||
}
|
||||
|
||||
getHomeInfo({RefreshController? refreshCtrl}) async {
|
||||
refreshData() {
|
||||
getHomeInfo();
|
||||
getMostSearchList();
|
||||
}
|
||||
|
||||
loadMoreData() {
|
||||
if (state.selDiscover == 0) {
|
||||
easyRefreshController.finishLoad(IndicatorResult.noMore);
|
||||
return;
|
||||
}
|
||||
state.pageIndex++;
|
||||
getCategoryVideoList();
|
||||
}
|
||||
|
||||
getHomeInfo() async {
|
||||
state.loadStatus = KtLoadStatusType.loading;
|
||||
try {
|
||||
ApiResponse res = await HttpClient().request(
|
||||
ApiResponse res = await KtHttpClient().request(
|
||||
KtApis.homeAllModules,
|
||||
method: HttpMethod.get,
|
||||
);
|
||||
refreshCtrl?.refreshCompleted();
|
||||
easyRefreshController.finishRefresh();
|
||||
|
||||
if (res.success) {
|
||||
state.loadStatus = KtLoadStatusType.loadSuccess;
|
||||
@ -57,7 +76,7 @@ class KtHomeLogic extends GetxController {
|
||||
.toList(),
|
||||
];
|
||||
} else if (item['module_key'] == 'week_highest_recommend') {
|
||||
state.trendingList = [
|
||||
state.hotList = [
|
||||
...item['data']
|
||||
.map(
|
||||
(item) =>
|
||||
@ -65,13 +84,24 @@ class KtHomeLogic extends GetxController {
|
||||
)
|
||||
.toList(),
|
||||
];
|
||||
int halfLength = state.trendingList.length ~/ 2;
|
||||
state.trendingTopList = state.trendingList.sublist(0, halfLength);
|
||||
state.trendingBottomList = state.trendingList.sublist(halfLength);
|
||||
} else if (item['module_key'] == 'category_navigation') {
|
||||
state.categoryList = [
|
||||
...item['data']
|
||||
.map(
|
||||
(item) => KtHomeCategoryBean.fromJson(
|
||||
item as Map<String, dynamic>,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
];
|
||||
if (state.selCategoryId == -1) {
|
||||
state.selCategoryId = state.categoryList.first.categoryId ?? -1;
|
||||
}
|
||||
getCategoryVideoList(isRefresh: true);
|
||||
}
|
||||
});
|
||||
|
||||
if (state.trendingList.isEmpty &&
|
||||
if (state.hotList.isEmpty &&
|
||||
state.bannerList.isEmpty &&
|
||||
state.arrivalList.isEmpty) {
|
||||
state.loadStatus = KtLoadStatusType.loadNoData;
|
||||
@ -85,4 +115,57 @@ class KtHomeLogic extends GetxController {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
getMostSearchList() async {
|
||||
ApiResponse res = await KtHttpClient().request(
|
||||
KtApis.searchHot,
|
||||
method: HttpMethod.get,
|
||||
);
|
||||
if (res.success) {
|
||||
state.mostSearchedList = [
|
||||
...res.data['list'].map((item) => KtShortVideoBean.fromJson(item)),
|
||||
];
|
||||
if (state.mostSearchedList.length > 3) {
|
||||
state.mostSearchedList = state.mostSearchedList.sublist(0, 3);
|
||||
}
|
||||
update(['trend']);
|
||||
}
|
||||
}
|
||||
|
||||
getCategoryVideoList({bool isRefresh = false}) async {
|
||||
if (isRefresh) {
|
||||
state.pageIndex = 1;
|
||||
state.categoryVideoList.clear();
|
||||
update();
|
||||
}
|
||||
Map<String, dynamic> params = {
|
||||
'current_page': state.pageIndex,
|
||||
'page_size': 20,
|
||||
};
|
||||
if (state.selCategoryId != -1) {
|
||||
params.putIfAbsent('category_id', () => state.selCategoryId);
|
||||
}
|
||||
|
||||
ApiResponse res = await KtHttpClient().request(
|
||||
KtApis.homeVideoList,
|
||||
method: HttpMethod.get,
|
||||
queryParameters: params,
|
||||
);
|
||||
easyRefreshController.finishRefresh();
|
||||
easyRefreshController.finishLoad();
|
||||
if (res.success) {
|
||||
List<KtShortVideoBean> list = [
|
||||
...res.data['list']
|
||||
.map(
|
||||
(item) => KtShortVideoBean.fromJson(item as Map<String, dynamic>),
|
||||
)
|
||||
.toList(),
|
||||
];
|
||||
if (list.length < 20) {
|
||||
easyRefreshController.finishLoad(IndicatorResult.noMore);
|
||||
}
|
||||
state.categoryVideoList.addAll(list);
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,23 @@
|
||||
import '../../kt_model/kt_home_category_bean.dart';
|
||||
import '../../kt_model/kt_short_video_bean.dart';
|
||||
import '../../kt_widgets/kt_status_widget.dart';
|
||||
|
||||
class KtHomeState {
|
||||
KtLoadStatusType loadStatus = KtLoadStatusType.loading;
|
||||
List<KtShortVideoBean> topPickList = [];
|
||||
List<KtShortVideoBean> trendingList = [];
|
||||
List<KtShortVideoBean> trendingTopList = [];
|
||||
List<KtShortVideoBean> trendingBottomList = [];
|
||||
List<KtShortVideoBean> hotList = [];
|
||||
List<KtShortVideoBean> arrivalList = [];
|
||||
List<KtShortVideoBean> bannerList = [];
|
||||
List<KtShortVideoBean> mostSearchedList = [];
|
||||
|
||||
List<KtHomeCategoryBean> categoryList = [];
|
||||
List<KtShortVideoBean> categoryVideoList = [];
|
||||
num selCategoryId = -1;
|
||||
|
||||
int pageIndex = 1;
|
||||
int selDiscover = 0;
|
||||
String selType = 'Hot & Rising';
|
||||
List typeList = ['Hot & Rising', 'Top Charts', 'Fresh Drops'];
|
||||
|
||||
bool showVideo = true;
|
||||
bool hasSubCoin = false;
|
||||
|
161
lib/kt_pages/kt_mine/kt_about_us_page.dart
Normal file
@ -0,0 +1,161 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_kinetra/kt_utils/kt_string_extend.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../dio_cilent/kt_apis.dart';
|
||||
import '../../kt_utils/kt_device_info_utils.dart';
|
||||
import '../kt_webview_page.dart';
|
||||
|
||||
class KtAboutUsPage extends StatefulWidget {
|
||||
const KtAboutUsPage({super.key});
|
||||
|
||||
@override
|
||||
State<KtAboutUsPage> createState() => _AboutUsPageState();
|
||||
}
|
||||
|
||||
class _AboutUsPageState extends State<KtAboutUsPage> {
|
||||
String versionNum = '';
|
||||
String appName = 'Kinetra';
|
||||
final List serviceList = [
|
||||
{'title': 'Privacy Policy', 'url': KtApis.WEB_SITE_PRIVATE},
|
||||
{'title': 'User Agreement', 'url': KtApis.WEB_SITE_POLICY},
|
||||
{'title': 'About Us', 'url': KtApis.WEB_SITE_INDEX},
|
||||
];
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Container(
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('bg2.png'.ktIcon),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
width: ScreenUtil().screenWidth,
|
||||
height: ScreenUtil().screenHeight,
|
||||
child: SafeArea(
|
||||
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(
|
||||
'About',
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w700,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
Container(width: 24.w),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 40.w),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(20.r),
|
||||
child: Image.asset(
|
||||
'ic_logo.png'.ktIcon,
|
||||
width: 84.w,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 7.w),
|
||||
Text(
|
||||
appName,
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Version: $versionNum',
|
||||
style: TextStyle(fontSize: 12.sp, color: Colors.black),
|
||||
),
|
||||
SizedBox(height: 40.w),
|
||||
...serviceList.map(
|
||||
(item) => Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
if (item['title'] == 'About Us') {
|
||||
_openUrl(item['url'].toString());
|
||||
return;
|
||||
}
|
||||
Get.to(
|
||||
() => KtWebViewPage(
|
||||
url: item['url'].toString(),
|
||||
title: item['title'],
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
padding: EdgeInsets.symmetric(horizontal: 30.w),
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
item['title'].toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Image.asset(
|
||||
'ic_right_arrow.png'.ktIcon,
|
||||
width: 10.w,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.symmetric(
|
||||
horizontal: 30.w,
|
||||
vertical: 20.w,
|
||||
),
|
||||
color: Colors.black.withValues(alpha: .1),
|
||||
width: double.infinity,
|
||||
height: 1 / MediaQuery.of(context).devicePixelRatio,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
initData();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
initData() {
|
||||
versionNum = KtDeviceInfoUtil().appVersion ?? '';
|
||||
}
|
||||
|
||||
void _openUrl(String url) async {
|
||||
try {
|
||||
final encodedUrl = Uri.encodeFull(url);
|
||||
await launchUrl(
|
||||
Uri.parse(encodedUrl),
|
||||
mode: LaunchMode.externalApplication,
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint('---err:$e');
|
||||
}
|
||||
}
|
||||
}
|
43
lib/kt_pages/kt_mine/kt_webview.dart
Normal file
@ -0,0 +1,43 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
class KtKtWebViewPage extends StatefulWidget {
|
||||
final String url;
|
||||
final String? title;
|
||||
|
||||
const KtKtWebViewPage({super.key, required this.url, this.title});
|
||||
|
||||
@override
|
||||
State<KtKtWebViewPage> createState() => _KtWebViewPageState();
|
||||
}
|
||||
|
||||
class _KtWebViewPageState extends State<KtKtWebViewPage> {
|
||||
late final WebViewController _controller;
|
||||
bool _isLoading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onPageFinished: (_) => setState(() => _isLoading = false),
|
||||
),
|
||||
)
|
||||
..loadRequest(Uri.parse(widget.url));
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(widget.title ?? '')),
|
||||
body: Stack(
|
||||
children: [
|
||||
WebViewWidget(controller: _controller),
|
||||
if (_isLoading) const Center(child: CircularProgressIndicator()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ class KtMineLogic extends GetxController {
|
||||
|
||||
getUserInfo() async {
|
||||
try {
|
||||
ApiResponse res = await HttpClient().request(
|
||||
ApiResponse res = await KtHttpClient().request(
|
||||
KtApis.getCustomerInfo,
|
||||
method: HttpMethod.get,
|
||||
);
|
||||
|
@ -1,8 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_kinetra/kt_pages/kt_mine/logic.dart';
|
||||
import 'package:flutter_kinetra/kt_pages/kt_routes.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:pull_to_refresh/pull_to_refresh.dart';
|
||||
|
||||
import '../../dio_cilent/kt_apis.dart';
|
||||
import '../kt_webview_page.dart';
|
||||
|
||||
class KtMinePage extends StatefulWidget {
|
||||
const KtMinePage({super.key});
|
||||
@ -12,6 +18,9 @@ class KtMinePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _KtMinePageState extends State<KtMinePage> {
|
||||
final logic = Get.put(KtMineLogic());
|
||||
final state = Get.find<KtMineLogic>().state;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -25,6 +34,217 @@ class _KtMinePageState extends State<KtMinePage> {
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: GetBuilder<KtMineLogic>(
|
||||
builder: (ctrl) {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
'Profile',
|
||||
style: TextStyle(
|
||||
fontSize: 18.sp,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w800,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SmartRefresher(
|
||||
controller: logic.refreshController,
|
||||
enablePullDown: true,
|
||||
onRefresh: logic.getUserInfo,
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: ScreenUtil().screenWidth - 30.w,
|
||||
margin: EdgeInsets.only(top: 22.w),
|
||||
padding: EdgeInsets.fromLTRB(6.w, 13.w, 6.w, 6.w),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('ic_mine_top_bg.png'.ktIcon),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Welcome Back, visitor!',
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(
|
||||
vertical: 17.w,
|
||||
horizontal: 13.w,
|
||||
),
|
||||
margin: EdgeInsets.only(top: 13.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(14.w),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset(
|
||||
'ic_avatar.png'.ktIcon,
|
||||
width: 56.w,
|
||||
),
|
||||
SizedBox(width: 13.w),
|
||||
Column(
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.start,
|
||||
children: [
|
||||
Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
Image.asset(
|
||||
'text_bg_discover.png'.ktIcon,
|
||||
height: 17.w,
|
||||
width: 55.w,
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: 2.w,
|
||||
),
|
||||
child: Text(
|
||||
KtUtils.isEmpty(
|
||||
state.userInfo.familyName,
|
||||
)
|
||||
? 'Visitor'
|
||||
: state
|
||||
.userInfo
|
||||
.familyName ??
|
||||
'',
|
||||
style: TextStyle(
|
||||
fontFamily: 'PingFang SC',
|
||||
fontSize: 18.sp,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'ID: ${state.userInfo.customerId}',
|
||||
style: TextStyle(
|
||||
fontFamily: 'PingFang SC',
|
||||
fontSize: 12.sp,
|
||||
color: Color(0xFF5E5E5E),
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 30.w),
|
||||
settingsView(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget settingsView() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Settings & More',
|
||||
style: TextStyle(
|
||||
fontSize: 15.sp,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12.w),
|
||||
Container(
|
||||
width: ScreenUtil().screenWidth - 36.w,
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(18.w),
|
||||
image: DecorationImage(
|
||||
image: AssetImage('setting_bg.png'.ktIcon),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
handleItem(
|
||||
'ic_privacy.png',
|
||||
'Privacy Policy',
|
||||
() => Get.to(
|
||||
() => KtWebViewPage(
|
||||
url: KtApis.WEB_SITE_PRIVATE,
|
||||
title: "Privacy Policy",
|
||||
),
|
||||
),
|
||||
),
|
||||
handleItem(
|
||||
'ic_agreement.png',
|
||||
'User Agreement',
|
||||
() => Get.to(
|
||||
() => KtWebViewPage(
|
||||
url: KtApis.WEB_SITE_POLICY,
|
||||
title: "User Agreement",
|
||||
),
|
||||
),
|
||||
),
|
||||
handleItem(
|
||||
'ic_help_center.png',
|
||||
'Help Center',
|
||||
() => Get.toNamed(KtRoutes.search),
|
||||
),
|
||||
handleItem(
|
||||
'ic_about_us.png',
|
||||
'About Us',
|
||||
() => Get.toNamed(KtRoutes.aboutUs),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget handleItem(String icon, String title, Function func) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
func.call();
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 15.w),
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
children: [
|
||||
Image.asset(icon.ktIcon, width: 20.w),
|
||||
SizedBox(width: 10.w),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14.sp,
|
||||
color: Colors.black,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Image.asset('ic_right_arrow.png'.ktIcon, width: 14.w),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
158
lib/kt_pages/kt_my_list/kt_my_chest_page.dart
Normal file
@ -0,0 +1,158 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_kinetra/kt_model/kt_short_video_bean.dart';
|
||||
import 'package:flutter_kinetra/kt_utils/kt_string_extend.dart';
|
||||
import 'package:flutter_kinetra/kt_widgets/kt_status_widget.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||
|
||||
import '../../kt_widgets/kt_network_image.dart';
|
||||
import '../kt_routes.dart';
|
||||
import 'logic.dart';
|
||||
|
||||
class KtMyChestPage extends StatefulWidget {
|
||||
const KtMyChestPage({super.key});
|
||||
|
||||
@override
|
||||
State<KtMyChestPage> createState() => _KtMyChestPageState();
|
||||
}
|
||||
|
||||
class _KtMyChestPageState extends State<KtMyChestPage> {
|
||||
final logic = Get.put(MyListLogic());
|
||||
final state = Get.find<MyListLogic>().state;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
logic.getCollectList(refresh: true);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: ScreenUtil().screenWidth,
|
||||
height: ScreenUtil().screenHeight,
|
||||
padding: EdgeInsets.only(top: ScreenUtil().statusBarHeight),
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('bg2.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 Treasure Chest',
|
||||
style: TextStyle(
|
||||
fontSize: 16.sp,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
Container(width: 24.w),
|
||||
],
|
||||
),
|
||||
GetBuilder<MyListLogic>(
|
||||
builder: (ctrl) {
|
||||
if (state.loadStatus == KtLoadStatusType.loading) {
|
||||
return Container();
|
||||
}
|
||||
|
||||
if (state.favoriteList.isEmpty &&
|
||||
state.loadStatus != KtLoadStatusType.loading) {
|
||||
return Container(
|
||||
margin: EdgeInsets.only(top: 60.w),
|
||||
child: KtStatusWidget(
|
||||
type: KtErrorStatusType.nothingYet,
|
||||
onPressed: logic.initData,
|
||||
),
|
||||
);
|
||||
}
|
||||
return Expanded(
|
||||
child: SmartRefresher(
|
||||
controller: logic.chestRefreshCtrl,
|
||||
enablePullUp: true,
|
||||
enablePullDown: true,
|
||||
onRefresh: () => logic.getCollectList(refresh: true),
|
||||
onLoading: () => logic.getCollectList(loadMore: true),
|
||||
child: GridView.builder(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: 16.w,
|
||||
left: 15.w,
|
||||
right: 15.w,
|
||||
),
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 3,
|
||||
mainAxisSpacing: 18.w,
|
||||
crossAxisSpacing: 6.w,
|
||||
childAspectRatio: 111 / 168,
|
||||
),
|
||||
itemCount: state.favoriteList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
KtShortVideoBean video = state.favoriteList[index];
|
||||
return GestureDetector(
|
||||
onTap: () => Get.toNamed(
|
||||
KtRoutes.shortVideo,
|
||||
arguments: {
|
||||
'shortPlayId': video.shortPlayId,
|
||||
'imageUrl': video.imageUrl ?? '',
|
||||
},
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Stack(
|
||||
children: [
|
||||
KtNetworkImage(
|
||||
imageUrl: video.imageUrl ?? '',
|
||||
width: 111.w,
|
||||
height: 148.w,
|
||||
borderRadius: BorderRadius.circular(12.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(
|
||||
width: 111.w,
|
||||
child: Text(
|
||||
video.name ?? '',
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 12.sp,
|
||||
color: Color(0xFF1E1E20),
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|