feat: 播放处理,个人中心,
BIN
assets/mine/copy.png
Normal file
|
After Width: | Height: | Size: 306 B |
BIN
assets/mine/def_avatar.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
assets/mine/little_left.png
Normal file
|
After Width: | Height: | Size: 231 B |
BIN
assets/mine/little_right.png
Normal file
|
After Width: | Height: | Size: 203 B |
BIN
assets/mine/mine_top.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
assets/mine/section_bg1.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
assets/mine/section_bg2.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/mine/section_bg3.png
Normal file
|
After Width: | Height: | Size: 1018 B |
BIN
assets/mine/section_top_bg.png
Normal file
|
After Width: | Height: | Size: 389 B |
BIN
assets/pics/cate_sel_left.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/pics/cate_sel_right.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/pics/esp_bg.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
assets/pics/play_tag.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
assets/pics/rank1.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/pics/rank2.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
assets/pics/rank3.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
@ -1,4 +1,3 @@
|
||||
|
||||
import 'package:skywood/global/request/api_const.dart';
|
||||
import 'package:skywood/global/request/app_request.dart';
|
||||
import 'package:skywood/global/tool/sky_log.dart';
|
||||
@ -8,7 +7,6 @@ import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_home.dart';
|
||||
|
||||
class PlayClient {
|
||||
|
||||
static Future<List<SkywoodHome>> getHomeData() {
|
||||
return AppRequest.get(ApiStringPlay.getHomeModules).then((value) {
|
||||
if (value.isSuccess) {
|
||||
@ -42,6 +40,20 @@ class PlayClient {
|
||||
});
|
||||
}
|
||||
|
||||
static Future<List<SkywoodDrama>> getCategoriesDetail({
|
||||
required int categoryId,
|
||||
int page = 1,
|
||||
int pageSize = 20,
|
||||
}) {
|
||||
return AppRequest.fetchPage<SkywoodDrama>(
|
||||
ApiStringPlay.getCategoriesDetail,
|
||||
page: page,
|
||||
pageSize: pageSize,
|
||||
extraParams: {'category_id': categoryId},
|
||||
fromJson: (json) => SkywoodDrama.fromJson(json),
|
||||
);
|
||||
}
|
||||
|
||||
// getSearchHots
|
||||
static Future<List<SkywoodDrama>> getSearchHots() {
|
||||
return AppRequest.get(ApiStringPlay.getSearchHots).then((value) {
|
||||
@ -54,29 +66,53 @@ class PlayClient {
|
||||
});
|
||||
}
|
||||
|
||||
// search
|
||||
static Future<List<SkywoodDrama>> search({required String keyword}) {
|
||||
return AppRequest.get(ApiStringPlay.getSearch, queryParameters: {
|
||||
'search': keyword,
|
||||
}).then((value) {
|
||||
if (value.isSuccess) {
|
||||
SkyLog.print('getSearchHots: ${value.data}');
|
||||
List list = parseDataForList(value.data);
|
||||
return list.map((e) => SkywoodDrama.fromJson(e)).toList();
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
//getCollections
|
||||
static Future<List> getCollections({
|
||||
int page = 1, int pageSize = 10
|
||||
static Future<List<SkywoodDrama>> getCollections({
|
||||
int page = 1,
|
||||
int pageSize = 10,
|
||||
}) {
|
||||
Map<String, dynamic> params = _setPage(page: page, pageSize: pageSize);
|
||||
return AppRequest.get(ApiStringPlay.getCollections, queryParameters: params).then((value) {
|
||||
return AppRequest.get(
|
||||
ApiStringPlay.getCollections,
|
||||
queryParameters: params,
|
||||
).then((value) {
|
||||
if (value.isSuccess) {
|
||||
SkyLog.print('getCollections: ${value.data}');
|
||||
|
||||
List list = parseDataForList(value.data);
|
||||
return list.map((e) => SkywoodDrama.fromJson(e)).toList();
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
// getHistory
|
||||
static Future<List> getHistory({
|
||||
int page = 1, int pageSize = 10
|
||||
static Future<List<SkywoodDrama>> getHistory({
|
||||
int page = 1,
|
||||
int pageSize = 10,
|
||||
}) {
|
||||
Map<String, dynamic> params = _setPage(page: page, pageSize: pageSize);
|
||||
return AppRequest.get(ApiStringPlay.getHistories, queryParameters: params).then((value) {
|
||||
return AppRequest.get(
|
||||
ApiStringPlay.getHistories,
|
||||
queryParameters: params,
|
||||
).then((value) {
|
||||
if (value.isSuccess) {
|
||||
SkyLog.print('getHistory: ${value.data}');
|
||||
|
||||
List list = parseDataForList(value.data);
|
||||
return list.map((e) => SkywoodDrama.fromJson(e)).toList();
|
||||
}
|
||||
return [];
|
||||
});
|
||||
@ -90,19 +126,48 @@ class PlayClient {
|
||||
Map<String, dynamic> params = {};
|
||||
params['short_play_id'] = shortPlayId;
|
||||
params['short_play_video_id'] = shortPlayVideoId;
|
||||
return AppRequest.get(ApiStringPlay.getVideoDetail, queryParameters: params).then((value) {
|
||||
return AppRequest.get(
|
||||
ApiStringPlay.getVideoDetail,
|
||||
queryParameters: params,
|
||||
).then((value) {
|
||||
if (value.isSuccess) {
|
||||
SkyLog.print('getVideoDetail: ${value.data}');
|
||||
SkyLog.print('getVideoDetail: ${value.data['data']}');
|
||||
return SkywoodDetail.fromJson(value.data['data']);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
// collect
|
||||
static Future<bool> collect({
|
||||
required int shortPlayId,
|
||||
required int videoId,
|
||||
}) {
|
||||
Map<String, dynamic> params = {};
|
||||
params['short_play_id'] = shortPlayId;
|
||||
params['video_id'] = videoId;
|
||||
return AppRequest.post(ApiStringPlay.collect, data: params).then((value) {
|
||||
SkyLog.print('collecttttt: ${value.data}');
|
||||
if (value.isSuccess) {}
|
||||
return value.isSuccess;
|
||||
});
|
||||
}
|
||||
|
||||
// cancelCollect
|
||||
static Future<bool> cancelCollect({required int shortPlayId}) {
|
||||
Map<String, dynamic> params = {};
|
||||
params['short_play_id'] = shortPlayId;
|
||||
return AppRequest.post(ApiStringPlay.cancelCollect, data: params).then((
|
||||
value,
|
||||
) {
|
||||
SkyLog.print('cancelCollectttt: ${value.data}');
|
||||
if (value.isSuccess) {}
|
||||
return value.isSuccess;
|
||||
});
|
||||
}
|
||||
|
||||
static Map<String, dynamic> _setPage({int page = 1, int pageSize = 10}) {
|
||||
return {
|
||||
'current_page': page,
|
||||
'page_size': pageSize,
|
||||
};
|
||||
return {'current_page': page, 'page_size': pageSize};
|
||||
}
|
||||
|
||||
static List parseDataForList(Map<String, dynamic> data) {
|
||||
@ -110,4 +175,4 @@ class PlayClient {
|
||||
List? listData = map['list'];
|
||||
return listData ?? [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:skywood/global/request/api_const.dart';
|
||||
import 'package:skywood/global/request/app_request.dart';
|
||||
import 'package:skywood/global/tool/sky_log.dart';
|
||||
import 'package:skywood/utils/models/user/register_bean.dart';
|
||||
import 'package:skywood/utils/models/user/skywood_user.dart';
|
||||
|
||||
class UserClient {
|
||||
|
||||
@ -27,4 +28,14 @@ class UserClient {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
static Future<SkywoodUser?> getUserInfo() {
|
||||
return AppRequest.get(ApiString.userInfo).then((value) {
|
||||
if (value.isSuccess) {
|
||||
SkyLog.print(value.data, tag: 'getUserInfo');
|
||||
return SkywoodUser.fromJson(value.data['data']);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -57,15 +57,15 @@ class ApiString {
|
||||
|
||||
class ApiStringPlay {
|
||||
// getRecommandsList GET /getRecommands 推荐列表,分页参数 current_page/page_size,可带 revolution
|
||||
// getVideoDetail GET /getVideoDetails 获取剧详情,query: short_play_id, video_id
|
||||
/// getVideoDetail GET /getVideoDetails 获取剧详情,query: short_play_id, video_id
|
||||
// getDetailsRecommand GET /getDetailsRecommand 详情页推荐列表;注意方法参数 page/pageSize/data 当前没有传入请求
|
||||
static const String getRecommandsList = "/getRecommands";
|
||||
static const String getVideoDetail = "/getVideoDetails";
|
||||
static const String getDetailsRecommand = "/getDetailsRecommand";
|
||||
|
||||
/// getHomeModules GET /home/all-modules 首页全部模块数据
|
||||
///=== getHomeModules GET /home/all-modules 首页全部模块数据
|
||||
// getRankingList POST /homeRanking 首页榜单,body 带 type,支持 most_trending/top_searched/new_releases
|
||||
/// getCategoriesList GET /getCategories 分类列表
|
||||
///=== getCategoriesList GET /getCategories 分类列表
|
||||
// getCategoriesDetail GET /videoList 分类详情/剧列表,分页 + extraParams
|
||||
// getWaterflowList POST /newShortPlay 首页瀑布流,分页 + extraParams
|
||||
static const String getHomeModules = "/home/all-modules";
|
||||
@ -76,10 +76,10 @@ class ApiStringPlay {
|
||||
|
||||
/// getSearchHots GET /search/hots 搜索热门列表
|
||||
// getSearch GET /search 关键词搜索,query: search
|
||||
// collect POST /collect 收藏,body: short_play_id, video_id
|
||||
// cancelCollect POST /cancelCollect 取消收藏,body: short_play_id
|
||||
// getMyCollections GET /myCollections 我的收藏列表,分页
|
||||
// getMyHistorys GET /myHistorys 播放历史列表,分页
|
||||
///==== collect POST /collect 收藏,body: short_play_id, video_id
|
||||
///==== cancelCollect POST /cancelCollect 取消收藏,body: short_play_id
|
||||
///==== getMyCollections GET /myCollections 我的收藏列表,分页
|
||||
///==== getMyHistorys GET /myHistorys 播放历史列表,分页
|
||||
// getNoticeNum POST /noticeNum 获取通知数量
|
||||
// versionControl GET /customer/versionControl 版本检查
|
||||
// getLanguges GET /languges 获取语言列表,接口名拼写就是 languges
|
||||
|
||||
@ -4,6 +4,7 @@ import 'package:skywood/pages/home/home_page.dart';
|
||||
import 'package:skywood/pages/like/like_page.dart';
|
||||
import 'package:skywood/pages/mine/mine_page.dart';
|
||||
import 'package:skywood/pages/search/page/search_page.dart';
|
||||
import 'package:skywood/utils/extend/app_keep_alive.dart';
|
||||
import 'package:skywood/utils/tools/help_img.dart';
|
||||
|
||||
class MainRoot extends StatefulWidget {
|
||||
@ -17,10 +18,10 @@ class MainRoot extends StatefulWidget {
|
||||
|
||||
class _MainRootState extends State<MainRoot> {
|
||||
final List<Widget> _pages = const [
|
||||
HomePage(),
|
||||
SearchPage(),
|
||||
AppKeepAlive(child: HomePage()),
|
||||
AppKeepAlive(child: SearchPage()),
|
||||
LikePage(),
|
||||
MinePage(),
|
||||
AppKeepAlive(child: MinePage()),
|
||||
];
|
||||
|
||||
final _pageController = PageController();
|
||||
|
||||
318
lib/pages/home/cate_page.dart
Normal file
@ -0,0 +1,318 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:skywood/global/client/play_client.dart';
|
||||
import 'package:skywood/main/lib_file.dart';
|
||||
import 'package:skywood/pages/home/players/page/drama_detail_page.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_category.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||
import 'package:skywood/utils/tools/sky_time_tool.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||
|
||||
class CatePage extends StatefulWidget {
|
||||
const CatePage({super.key});
|
||||
|
||||
@override
|
||||
State<CatePage> createState() => _CatePageState();
|
||||
}
|
||||
|
||||
class _CatePageState extends State<CatePage> {
|
||||
final List<SkywoodCategory> _categories = [];
|
||||
final List<SkywoodDrama> _dramas = [];
|
||||
int _selectedIndex = 0;
|
||||
int? _loadingCategoryId;
|
||||
bool _loadingCategories = true;
|
||||
bool _loadingDramas = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadCategories();
|
||||
}
|
||||
|
||||
Future<void> _loadCategories() async {
|
||||
List<SkywoodCategory> categories = [];
|
||||
try {
|
||||
categories = await PlayClient.getCategoriesList();
|
||||
} catch (_) {
|
||||
if (!mounted) return;
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_categories
|
||||
..clear()
|
||||
..addAll(categories);
|
||||
_loadingCategories = false;
|
||||
});
|
||||
|
||||
if (categories.isNotEmpty) {
|
||||
_loadCategoryDetail(categories.first.id);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadCategoryDetail(int categoryId) async {
|
||||
_loadingCategoryId = categoryId;
|
||||
setState(() {
|
||||
_loadingDramas = true;
|
||||
_dramas.clear();
|
||||
});
|
||||
|
||||
List<SkywoodDrama> dramas = [];
|
||||
try {
|
||||
dramas = await PlayClient.getCategoriesDetail(categoryId: categoryId);
|
||||
} catch (_) {
|
||||
if (!mounted || _loadingCategoryId != categoryId) return;
|
||||
}
|
||||
|
||||
if (!mounted || _loadingCategoryId != categoryId) return;
|
||||
|
||||
setState(() {
|
||||
_dramas.addAll(dramas);
|
||||
_loadingDramas = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.white,
|
||||
body: SafeArea(
|
||||
bottom: false,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeader(),
|
||||
Expanded(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildCategoryList(),
|
||||
Expanded(child: _buildDramaList()),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return SizedBox(
|
||||
height: 49.h,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
left: 13.w,
|
||||
top: 16.h,
|
||||
child: HelpImg.asset(
|
||||
ConstImg.back,
|
||||
width: 24.w,
|
||||
height: 24.w,
|
||||
).addInkWell(onTap: () => HelpNav.back()),
|
||||
),
|
||||
Positioned(
|
||||
left: 94.w,
|
||||
top: 22.h,
|
||||
child: SkyText(
|
||||
text: 'Explore Genres',
|
||||
color: CommonColor.black,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w800,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCategoryList() {
|
||||
return Container(
|
||||
width: 86.w,
|
||||
height: double.infinity,
|
||||
padding: EdgeInsets.only(top: 12.h, bottom: 18.h),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFF2FFF0),
|
||||
borderRadius: BorderRadius.only(topRight: Radius.circular(8.r)),
|
||||
),
|
||||
child: _loadingCategories
|
||||
? const SizedBox.shrink()
|
||||
: ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
itemCount: _categories.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildCategoryItem(index);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCategoryItem(int index) {
|
||||
final category = _categories[index];
|
||||
final selected = index == _selectedIndex;
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
if (selected) return;
|
||||
setState(() {
|
||||
_selectedIndex = index;
|
||||
});
|
||||
_loadCategoryDetail(category.id);
|
||||
},
|
||||
child: SizedBox(
|
||||
height: 47.h,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
if (selected)
|
||||
Positioned(
|
||||
left: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: HelpImg.asset(
|
||||
ConstImg.cateSelLeft,
|
||||
width: 20.w,
|
||||
height: 50.h,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
if (selected)
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
child: HelpImg.asset(
|
||||
ConstImg.cateSelRight,
|
||||
width: 20.w,
|
||||
height: 50.h,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.w),
|
||||
child: SkyText(
|
||||
text: category.name,
|
||||
color: selected
|
||||
? CommonColor.primacyColor
|
||||
: Colors.black.withAlpha(100),
|
||||
fontSize: 11,
|
||||
fontWeight: selected ? FontWeight.w700 : FontWeight.w500,
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2,
|
||||
lineHeight: 1.05,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDramaList() {
|
||||
if (_loadingDramas) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(color: CommonColor.primacyColor),
|
||||
);
|
||||
}
|
||||
|
||||
if (_dramas.isEmpty) {
|
||||
return Center(
|
||||
child: SkyText(
|
||||
text: 'No Data',
|
||||
color: Colors.black.withAlpha(100),
|
||||
fontSize: 13,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
padding: EdgeInsets.only(left: 8.w, right: 10.w, bottom: 24.h),
|
||||
itemCount: _dramas.length,
|
||||
separatorBuilder: (context, index) => SizedBox(height: 10.h),
|
||||
itemBuilder: (context, index) {
|
||||
return _DramaItem(drama: _dramas[index]);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DramaItem extends StatelessWidget {
|
||||
const _DramaItem({required this.drama});
|
||||
|
||||
final SkywoodDrama drama;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 80.h,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
HelpImg.networkRadius(
|
||||
drama.imageUrl,
|
||||
width: 64.w,
|
||||
height: 80.h,
|
||||
fit: BoxFit.cover,
|
||||
borderRadius: BorderRadius.circular(4.r),
|
||||
),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 2.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SkyText(
|
||||
text: drama.name,
|
||||
color: CommonColor.black,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w700,
|
||||
maxLines: 2,
|
||||
lineHeight: 1.08,
|
||||
),
|
||||
SizedBox(height: 7.h),
|
||||
SkyText(
|
||||
text: drama.description,
|
||||
color: Colors.black.withAlpha(92),
|
||||
fontSize: 9,
|
||||
maxLines: 2,
|
||||
lineHeight: 1.2,
|
||||
),
|
||||
const Spacer(),
|
||||
Row(
|
||||
children: [
|
||||
_TipView(icon: ConstImg.star, text: '10.0'),
|
||||
SizedBox(width: 12.w),
|
||||
_TipView(
|
||||
icon: ConstImg.look,
|
||||
text: SkyTimeTool.formatNum(drama.watchTotal),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
).addInkWell(onTap: () => HelpNav.to(DramaDetailPage(dramaBean: drama)));
|
||||
}
|
||||
}
|
||||
|
||||
class _TipView extends StatelessWidget {
|
||||
const _TipView({required this.icon, required this.text});
|
||||
|
||||
final String icon;
|
||||
final String text;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
HelpImg.asset(icon, width: 8.w, height: 8.w),
|
||||
SizedBox(width: 2.w),
|
||||
SkyText(text: text, color: Colors.black.withAlpha(110), fontSize: 8),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:skywood/global/client/play_client.dart';
|
||||
import 'package:skywood/main/lib_file.dart';
|
||||
import 'package:skywood/pages/home/players/widget/player_bottom_widget.dart';
|
||||
import 'package:skywood/pages/home/players/widget/sky_player_view.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_detail.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_home.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_app_bar.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||
|
||||
class DramaDetailPage extends StatefulWidget {
|
||||
const DramaDetailPage({super.key,
|
||||
@ -20,28 +24,30 @@ class DramaDetailPage extends StatefulWidget {
|
||||
|
||||
class _DramaDetailPageState extends State<DramaDetailPage> {
|
||||
|
||||
SkywoodDetail? _detailBean;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// _getDetailDrama();
|
||||
_getDetailDrama();
|
||||
}
|
||||
|
||||
// void _getDetailDrama() {
|
||||
// PlayClient.getVideoDetail(
|
||||
// shortPlayId: widget.shortPlayId,
|
||||
// shortPlayVideoId: widget.shortPlayVideoId
|
||||
// ).then((value) {});
|
||||
// }
|
||||
void _getDetailDrama() {
|
||||
PlayClient.getVideoDetail(
|
||||
shortPlayId: widget.dramaBean.shortPlayId ?? 0,
|
||||
shortPlayVideoId: widget.dramaBean.shortPlayVideoId ?? 0
|
||||
).then((value) {
|
||||
setState(() {
|
||||
_detailBean = value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
appBar: SkyAppBar(
|
||||
title: widget.dramaBean.name,
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
body: _bodyWidget(),
|
||||
);
|
||||
}
|
||||
@ -50,11 +56,43 @@ class _DramaDetailPageState extends State<DramaDetailPage> {
|
||||
return Stack(
|
||||
children: [
|
||||
SizedBox(width: double.infinity, height: double.infinity),
|
||||
|
||||
if (widget.dramaBean.videoUrl != null)
|
||||
Positioned(
|
||||
bottom: 0, left: 0, right: 0, top: 0,
|
||||
child: SkyPlayerView(
|
||||
videoUrl: widget.dramaBean.videoUrl!,
|
||||
detailBean: _detailBean,
|
||||
dramaBean: widget.dramaBean,
|
||||
),
|
||||
),
|
||||
|
||||
Positioned(
|
||||
bottom: 0, left: 0, right: 0,
|
||||
child: PlayerBottomWidget(),
|
||||
),
|
||||
top: AppScreen.statusBarHeight + 5, left: 0, right: 0,
|
||||
child: _headerWidget(),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _headerWidget() {
|
||||
return Container(
|
||||
height: 40,
|
||||
padding: const EdgeInsets.only(right: 15, left: 5),
|
||||
child: Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: HelpImg.asset(ConstImg.back, width: 32),
|
||||
onPressed: () => HelpNav.back(),
|
||||
),
|
||||
Expanded(child: SkyText(text: widget.dramaBean.name, color: Colors.white, fontSize: 16,
|
||||
textAlign: TextAlign.center,
|
||||
fontWeight: FontWeight.bold,
|
||||
)),
|
||||
SizedBox(width: 40,)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
220
lib/pages/home/players/page/drama_esp_page.dart
Normal file
@ -0,0 +1,220 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:skywood/main/lib_file.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_detail.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_episode.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||
|
||||
class DramaEspPage extends StatefulWidget {
|
||||
const DramaEspPage({super.key, required this.detail, this.currentEpisode});
|
||||
|
||||
final SkywoodDetail detail;
|
||||
final int? currentEpisode;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
return _DramaEspPageState();
|
||||
}
|
||||
}
|
||||
|
||||
class _DramaEspPageState extends State<DramaEspPage> {
|
||||
late int _selectedEpisode;
|
||||
|
||||
SkywoodDrama? get _drama =>
|
||||
widget.detail.shortPlayInfo ?? widget.detail.videoInfo;
|
||||
|
||||
List<SkywoodEpisode> get _episodes => widget.detail.episodeList;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_selectedEpisode = widget.currentEpisode ?? _drama?.episode ?? 1;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double height = 520;
|
||||
|
||||
return SizedBox(
|
||||
width: AppScreen.screenWidth,
|
||||
height: 520,
|
||||
child: Stack(
|
||||
children: [
|
||||
HelpImg.asset(
|
||||
ConstImg.espBg,
|
||||
width: AppScreen.screenWidth,
|
||||
height: height,
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
Positioned.fill(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(22.w, 31.h, 22.w, 54.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildDramaInfo(),
|
||||
SizedBox(height: 12),
|
||||
Divider(
|
||||
height: 1,
|
||||
thickness: 0.5,
|
||||
color: Colors.black.withAlpha(18),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
SkyText(
|
||||
text: '$_episodeTotal EP',
|
||||
color: Colors.black,
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
SizedBox(height: 8),
|
||||
Expanded(child: _buildEpisodeGrid()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDramaInfo() {
|
||||
final SkywoodDrama? drama = _drama;
|
||||
final SkywoodDrama? info = widget.detail.videoInfo;
|
||||
final String imageUrl = drama?.imageUrl ?? '';
|
||||
|
||||
return SizedBox(
|
||||
height: 130.h,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
HelpImg.networkRadius(
|
||||
imageUrl,
|
||||
width: 96.w,
|
||||
height: 130.h,
|
||||
fit: BoxFit.cover,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
SizedBox(width: 16.w),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(top: 7.h),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SkyText(
|
||||
text: drama?.name ?? '',
|
||||
color: Colors.black,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w700,
|
||||
maxLines: 2,
|
||||
lineHeight: 1.12,
|
||||
),
|
||||
SizedBox(height: 10.h),
|
||||
SkyText(
|
||||
text: info?.description ?? '',
|
||||
color: Colors.black.withAlpha(118),
|
||||
fontSize: 14,
|
||||
maxLines: 2,
|
||||
lineHeight: 1.45,
|
||||
),
|
||||
SizedBox(height: 13.h),
|
||||
_buildTag(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTag() {
|
||||
final String name = _drama?.categoryList?.isNotEmpty == true
|
||||
? _drama!.categoryList!.first.name
|
||||
: (_drama?.tagType ?? '');
|
||||
|
||||
if (name.isEmpty) return const SizedBox.shrink();
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 8.w, vertical: 3.h),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFE7FFE5),
|
||||
border: Border.all(color: const Color(0xFF86D57A), width: 1),
|
||||
),
|
||||
child: SkyText(
|
||||
text: name,
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
overflow: TextOverflow.visible,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEpisodeGrid() {
|
||||
if (_episodes.isEmpty) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
physics: const BouncingScrollPhysics(),
|
||||
itemCount: _episodes.length,
|
||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 5,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 10,
|
||||
childAspectRatio: 61 / 50,
|
||||
),
|
||||
itemBuilder: (context, index) {
|
||||
return _buildEpisodeItem(_episodes[index]);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEpisodeItem(SkywoodEpisode episode) {
|
||||
final bool selected = episode.episode == _selectedEpisode;
|
||||
final bool selectedPlayable = selected && !episode.isLock;
|
||||
final bool selectedLocked = selected && episode.isLock;
|
||||
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_selectedEpisode = episode.episode;
|
||||
});
|
||||
},
|
||||
child: Container(
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: selectedPlayable
|
||||
? const Color(0xFF91F476)
|
||||
: const Color(0xFFF8F8F8),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: selectedPlayable
|
||||
? Colors.black
|
||||
: (selectedLocked
|
||||
? CommonColor.primacyColor
|
||||
: Colors.transparent),
|
||||
width: selected ? 1 : 0,
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: SkyText(
|
||||
text: episode.episode.toString(),
|
||||
color: Colors.black.withAlpha(220),
|
||||
fontSize: 20,
|
||||
fontWeight: selected ? FontWeight.w700 : FontWeight.w400,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
int get _episodeTotal {
|
||||
if (_drama?.episodeTotal != null && _drama!.episodeTotal > 0) {
|
||||
return _drama!.episodeTotal;
|
||||
}
|
||||
return _episodes.length;
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,13 @@ import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||
|
||||
class PlayerBottomWidget extends StatefulWidget {
|
||||
|
||||
const PlayerBottomWidget({super.key});
|
||||
const PlayerBottomWidget({super.key,
|
||||
// required this.totalTime,
|
||||
// required this.currentTime,
|
||||
});
|
||||
|
||||
// final String totalTime;
|
||||
// final String currentTime;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() {
|
||||
@ -92,4 +98,10 @@ class _PlayerBottomWidgetState extends State<PlayerBottomWidget> {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDuration(Duration duration) {
|
||||
final minutes = duration.inMinutes.remainder(60).toString().padLeft(2, '0');
|
||||
final seconds = duration.inSeconds.remainder(60).toString().padLeft(2, '0');
|
||||
return '$minutes:$seconds';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,396 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:skywood/global/client/play_client.dart';
|
||||
import 'package:skywood/main/lib_file.dart';
|
||||
import 'package:skywood/pages/home/players/page/drama_esp_page.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_detail.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
||||
/// 视频播放器
|
||||
class SkyPlayerView extends StatefulWidget {
|
||||
final String videoUrl;
|
||||
final bool autoPlay;
|
||||
final SkywoodDetail? detailBean;
|
||||
final SkywoodDrama dramaBean;
|
||||
|
||||
const SkyPlayerView({
|
||||
super.key,
|
||||
required this.videoUrl,
|
||||
this.autoPlay = true,
|
||||
this.detailBean,
|
||||
required this.dramaBean,
|
||||
});
|
||||
|
||||
@override
|
||||
State<SkyPlayerView> createState() => _VideoPlayerWidgetState();
|
||||
}
|
||||
|
||||
class _VideoPlayerWidgetState extends State<SkyPlayerView> {
|
||||
late VideoPlayerController _controller;
|
||||
bool _controllerCreated = false;
|
||||
bool _isPlaying = false;
|
||||
bool _isInitialized = false;
|
||||
bool _isSeeking = false;
|
||||
String? _errorMessage;
|
||||
Duration _currentPosition = Duration.zero;
|
||||
Duration _totalDuration = Duration.zero;
|
||||
bool _isLiked = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializePlayer();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(SkyPlayerView oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.videoUrl != widget.videoUrl) {
|
||||
_disposeController();
|
||||
_initializePlayer();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initializePlayer() async {
|
||||
setState(() {
|
||||
_isInitialized = false;
|
||||
_isPlaying = false;
|
||||
_errorMessage = null;
|
||||
_currentPosition = Duration.zero;
|
||||
_totalDuration = Duration.zero;
|
||||
_isLiked = widget.dramaBean.isCollect ?? false;
|
||||
});
|
||||
|
||||
final controller = VideoPlayerController.networkUrl(
|
||||
Uri.parse(widget.videoUrl),
|
||||
);
|
||||
_controller = controller;
|
||||
_controllerCreated = true;
|
||||
controller.addListener(_handlePlayerValueChanged);
|
||||
|
||||
try {
|
||||
await controller.initialize();
|
||||
if (!mounted || controller != _controller || !_controllerCreated) return;
|
||||
|
||||
if (widget.autoPlay) {
|
||||
await controller.play();
|
||||
}
|
||||
|
||||
_syncPlayerValue();
|
||||
} catch (error) {
|
||||
if (!mounted || controller != _controller) return;
|
||||
setState(() {
|
||||
_errorMessage = error.toString();
|
||||
_isInitialized = false;
|
||||
_isPlaying = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _handlePlayerValueChanged() {
|
||||
if (!mounted || !_controllerCreated) return;
|
||||
_syncPlayerValue();
|
||||
}
|
||||
|
||||
void _syncPlayerValue() {
|
||||
final value = _controller.value;
|
||||
final nextPosition = _isSeeking ? _currentPosition : value.position;
|
||||
final nextDuration = value.duration;
|
||||
final nextPlaying = value.isPlaying;
|
||||
|
||||
setState(() {
|
||||
_isInitialized = value.isInitialized;
|
||||
_isPlaying = nextPlaying;
|
||||
_currentPosition = nextPosition;
|
||||
_totalDuration = nextDuration;
|
||||
});
|
||||
}
|
||||
|
||||
void _disposeController() {
|
||||
if (!_controllerCreated) return;
|
||||
_controller.removeListener(_handlePlayerValueChanged);
|
||||
_controller.dispose();
|
||||
_controllerCreated = false;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_disposeController();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_errorMessage != null) {
|
||||
return _buildErrorView();
|
||||
}
|
||||
|
||||
return _isInitialized && _controllerCreated
|
||||
? _buildVideoContent()
|
||||
: _buildLoadingView();
|
||||
}
|
||||
|
||||
Widget _buildVideoContent() {
|
||||
return Stack(
|
||||
fit: StackFit.expand,
|
||||
children: [
|
||||
FittedBox(
|
||||
fit: BoxFit.cover,
|
||||
child: SizedBox(
|
||||
width: _controller.value.size.width,
|
||||
height: _controller.value.size.height,
|
||||
child: VideoPlayer(_controller),
|
||||
),
|
||||
),
|
||||
|
||||
Positioned.fill(
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.translucent,
|
||||
onTap: _togglePlay,
|
||||
child: Container(),
|
||||
),
|
||||
),
|
||||
|
||||
if (widget.detailBean != null)
|
||||
Positioned(bottom: 0, left: 0, right: 0, child: _buildBottomView()),
|
||||
|
||||
if (!_isPlaying)
|
||||
Center(
|
||||
child: GestureDetector(
|
||||
onTap: _togglePlay,
|
||||
child: Container(
|
||||
width: 60,
|
||||
height: 60,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withAlpha(204),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.play_arrow_rounded,
|
||||
color: Colors.black,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoadingView() {
|
||||
return Container(
|
||||
color: Colors.black,
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(color: Colors.white),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildErrorView() {
|
||||
return Container(
|
||||
color: Colors.black,
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: SkyText(
|
||||
text: 'Video failed to load',
|
||||
color: Colors.white.withAlpha(180),
|
||||
fontSize: 14,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _togglePlay() {
|
||||
if (!_isInitialized || !_controllerCreated) return;
|
||||
|
||||
if (_controller.value.isPlaying) {
|
||||
_controller.pause();
|
||||
} else {
|
||||
_controller.play();
|
||||
}
|
||||
}
|
||||
|
||||
void _collectionDrama() {
|
||||
if (_isLiked) {
|
||||
PlayClient.cancelCollect(shortPlayId: widget.dramaBean.shortPlayId??0).then((value) {
|
||||
setState(() {
|
||||
_isLiked = value;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
PlayClient.collect(
|
||||
shortPlayId: widget.dramaBean.shortPlayId??0,
|
||||
videoId: widget.dramaBean.shortPlayVideoId ?? 0,
|
||||
).then((value) {
|
||||
setState(() {
|
||||
_isLiked = value;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildBottomView() {
|
||||
SkywoodDetail detail = widget.detailBean!;
|
||||
final int currentEpisode = _currentEpisode(detail);
|
||||
final int totalEpisode = _episodeTotal(detail);
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => _collectionDrama(),
|
||||
child: HelpImg.asset(
|
||||
_isLiked ? ConstImg.liked : ConstImg.unlike,
|
||||
width: 40, height: 40
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Container(
|
||||
height: 36,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
color: Colors.white.withAlpha(30),
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
HelpImg.asset(ConstImg.playLt, width: 17, height: 17),
|
||||
const SizedBox(width: 10),
|
||||
SkyText(
|
||||
text: 'EP.$currentEpisode',
|
||||
fontSize: 13,
|
||||
color: Colors.white,
|
||||
),
|
||||
SkyText(
|
||||
text: ' / EP.${detail.episodeList.length}',
|
||||
fontSize: 13,
|
||||
color: Colors.white.withAlpha(126),
|
||||
),
|
||||
],
|
||||
),
|
||||
Icon(Icons.arrow_forward_ios, color: Colors.white, size: 13),
|
||||
],
|
||||
),
|
||||
).addInkWell(
|
||||
onTap: () {
|
||||
// HelpNav.showBottom(
|
||||
// DramaEspPage(detail: detail, currentEpisode: currentEpisode),
|
||||
// );
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) {
|
||||
return SizedBox(
|
||||
height: 520,
|
||||
child: DramaEspPage(
|
||||
detail: detail,
|
||||
currentEpisode: currentEpisode,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
_buildProgressBar(),
|
||||
const SizedBox(height: 7),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
SkyText(
|
||||
text: _formatDuration(_currentPosition),
|
||||
fontSize: 13,
|
||||
color: Colors.white,
|
||||
),
|
||||
SkyText(
|
||||
text: ' / ${_formatDuration(_totalDuration)}',
|
||||
fontSize: 13,
|
||||
color: Colors.white.withAlpha(126),
|
||||
),
|
||||
],
|
||||
),
|
||||
SkyText(text: 'x1.0', fontSize: 13, color: Colors.white),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProgressBar() {
|
||||
final int totalMilliseconds = _totalDuration.inMilliseconds;
|
||||
final double progress = totalMilliseconds <= 0
|
||||
? 0
|
||||
: (_currentPosition.inMilliseconds / totalMilliseconds).clamp(0.0, 1.0);
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 3,
|
||||
child: SliderTheme(
|
||||
data: SliderThemeData(
|
||||
trackHeight: 3, // ✅ 进度条高度 3
|
||||
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 1),
|
||||
overlayShape: const RoundSliderOverlayShape(overlayRadius: 3),
|
||||
activeTrackColor: Colors.white, // ✅ 已播放 = 白色
|
||||
inactiveTrackColor: Colors.white.withAlpha(126), // ✅ 未播放 = 灰色
|
||||
thumbColor: Colors.white,
|
||||
overlayColor: Colors.white.withAlpha(76),
|
||||
),
|
||||
child: Slider(
|
||||
padding: EdgeInsets.zero,
|
||||
value: progress,
|
||||
min: 0,
|
||||
max: 1,
|
||||
onChangeStart: (_) {
|
||||
_isSeeking = true;
|
||||
},
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_currentPosition = Duration(
|
||||
milliseconds: (totalMilliseconds * value).round(),
|
||||
);
|
||||
});
|
||||
},
|
||||
onChangeEnd: (value) {
|
||||
final position = Duration(
|
||||
milliseconds: (totalMilliseconds * value).round(),
|
||||
);
|
||||
_controller.seekTo(position);
|
||||
_isSeeking = false;
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDuration(Duration duration) {
|
||||
final minutes = duration.inMinutes.remainder(60).toString().padLeft(2, '0');
|
||||
final seconds = duration.inSeconds.remainder(60).toString().padLeft(2, '0');
|
||||
return '$minutes:$seconds';
|
||||
}
|
||||
|
||||
int _currentEpisode(SkywoodDetail detail) {
|
||||
final int? episode =
|
||||
detail.videoInfo?.episode ??
|
||||
detail.videoInfo?.currentEpisode ??
|
||||
detail.shortPlayInfo?.episode ??
|
||||
detail.shortPlayInfo?.currentEpisode;
|
||||
|
||||
if (episode != null && episode > 0) return episode;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _episodeTotal(SkywoodDetail detail) {
|
||||
return detail.episodeList.length;
|
||||
}
|
||||
}
|
||||
@ -52,7 +52,13 @@ class DailyQuestWidget extends StatelessWidget {
|
||||
child: Row(
|
||||
spacing: 5.w,
|
||||
children: List.generate(dramaList.length-1, (index) {
|
||||
return HelpImg.network(dramaList[index+1].imageUrl, width: 95.w, height: 50.h, fit: BoxFit.cover);
|
||||
final drama = dramaList[index+1];
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
HelpNav.to(DramaDetailPage(dramaBean: drama));
|
||||
},
|
||||
child: HelpImg.network(drama.imageUrl, width: 95.w, height: 50.h, fit: BoxFit.cover),
|
||||
);
|
||||
})
|
||||
),
|
||||
);
|
||||
|
||||
@ -3,6 +3,8 @@ import 'package:skywood/main/lib_file.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_category.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||
|
||||
import '../players/page/drama_detail_page.dart';
|
||||
|
||||
class HomeCateWidget extends StatelessWidget {
|
||||
|
||||
const HomeCateWidget({super.key,
|
||||
@ -25,7 +27,8 @@ class HomeCateWidget extends StatelessWidget {
|
||||
),
|
||||
itemCount: categories.length,
|
||||
itemBuilder: (context, index) {
|
||||
return _buildCateItem(categories[index], index);}
|
||||
return _buildCateItem(categories[index], index);
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -40,7 +43,9 @@ class HomeCateWidget extends StatelessWidget {
|
||||
child: _customText(category.name,),
|
||||
)
|
||||
],
|
||||
);
|
||||
).addInkWell(onTap: () {
|
||||
// HelpNav.to(DramaDetailPage(dramaBean: drama));
|
||||
});
|
||||
}
|
||||
|
||||
_customText(String text) {
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:skywood/main/lib_file.dart';
|
||||
|
||||
import '../../../utils/models/drama/skywood_drama.dart';
|
||||
import '../players/page/drama_detail_page.dart';
|
||||
|
||||
class HotCartridgeWidget extends StatelessWidget {
|
||||
|
||||
@ -38,6 +39,8 @@ class HotCartridgeWidget extends StatelessWidget {
|
||||
),
|
||||
HelpImg.asset(index<3?ConstImg.hotBg1:ConstImg.hotBg2, width: 110.w, height: 174.h)
|
||||
],
|
||||
);
|
||||
).addInkWell(onTap: () {
|
||||
HelpNav.to(DramaDetailPage(dramaBean: drama));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -67,7 +67,7 @@ class RankListWidget extends StatelessWidget {
|
||||
Widget _rankIndex(int index) {
|
||||
return SizedBox(
|
||||
width: 40.w,
|
||||
child: SkyText(text: formatNumber(index + 1),
|
||||
child: index < 3 ? _rank123(index) : SkyText(text: formatNumber(index + 1),
|
||||
fontSize: 15,
|
||||
color: CommonColor.black,
|
||||
fontFamily: 'Anton',
|
||||
@ -77,6 +77,16 @@ class RankListWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _rank123(int index) {
|
||||
String img = '';
|
||||
if (index == 0) img = ConstImg.rank1;
|
||||
if (index == 1) img = ConstImg.rank2;
|
||||
if (index == 2) img = ConstImg.rank3;
|
||||
return Padding(padding: EdgeInsets.only(right: 16.w),
|
||||
child: HelpImg.asset(img, width: 24.w),
|
||||
);
|
||||
}
|
||||
|
||||
String formatNumber(int number) {
|
||||
return number.toString().padLeft(2, '0');
|
||||
}
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:skywood/global/client/play_client.dart';
|
||||
import 'package:skywood/main/lib_file.dart';
|
||||
import 'package:skywood/pages/home/players/page/drama_detail_page.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||
|
||||
class CollectionPage extends StatefulWidget {
|
||||
|
||||
@ -14,16 +17,19 @@ class CollectionPage extends StatefulWidget {
|
||||
|
||||
class _CollectionPageState extends State<CollectionPage> {
|
||||
|
||||
List<SkywoodDrama> _collectionList = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
_getCollection();
|
||||
}
|
||||
|
||||
void _getCollection() {
|
||||
PlayClient.getCollections().then((value) {
|
||||
|
||||
setState(() {
|
||||
_collectionList = value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -40,16 +46,36 @@ class _CollectionPageState extends State<CollectionPage> {
|
||||
itemBuilder: (context, index) {
|
||||
return _buildItem(index);
|
||||
},
|
||||
itemCount: 5,
|
||||
itemCount: _collectionList.length,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildItem(int index) {
|
||||
final SkywoodDrama drama = _collectionList[index];
|
||||
return Stack(
|
||||
children: [
|
||||
HelpImg.asset(ConstImg.collectionBg, width: 100.w, height: 174.w, fit: BoxFit.fill,),
|
||||
|
||||
SizedBox(
|
||||
width: 100.w, height: 174.w,
|
||||
child: HelpImg.asset(ConstImg.collectionBg, width: 100.w, height: 174.w, fit: BoxFit.fill,),
|
||||
),
|
||||
Positioned(
|
||||
left: 5, right: 5, top: 32.w,
|
||||
child: HelpImg.network(drama.imageUrl, height: 110.w, width: 90.w, fit: BoxFit.fill,),
|
||||
),
|
||||
Positioned(
|
||||
bottom: 6, left: 2, right: 2,
|
||||
child: SkyText(text: drama.name,
|
||||
fontSize: 13,
|
||||
color: CommonColor.black,
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
right: 10, top: 15,
|
||||
child: HelpImg.asset(ConstImg.liked, width: 18, height: 18,),
|
||||
)
|
||||
],
|
||||
);
|
||||
).addInkWell(onTap: () {
|
||||
HelpNav.to(DramaDetailPage(dramaBean: drama));
|
||||
},);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,14 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:skywood/global/client/user_client.dart';
|
||||
import 'package:skywood/main/lib_file.dart';
|
||||
import 'package:skywood/pages/mine/widget/mine_section_view.dart';
|
||||
import 'package:skywood/pages/widgets/pixel_widgets.dart';
|
||||
import 'package:skywood/utils/models/user/skywood_user.dart';
|
||||
import 'package:skywood/utils/tools/app_toast.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||
|
||||
import '../widgets/common_bg_page.dart';
|
||||
|
||||
class MinePage extends StatefulWidget {
|
||||
const MinePage({super.key});
|
||||
@ -12,32 +20,92 @@ class MinePage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MainRootState extends State<MinePage> {
|
||||
|
||||
SkywoodUser? _userInfo;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_getUserData();
|
||||
}
|
||||
|
||||
void _getUserData() {
|
||||
UserClient.getUserInfo().then((value) {
|
||||
setState(() {
|
||||
_userInfo = value;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: PixelPageBackground(
|
||||
child: SafeArea(
|
||||
return CommonBgPage(
|
||||
child: SafeArea(
|
||||
bottom: false,
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(child: SizedBox(height: 12.h)),
|
||||
SliverToBoxAdapter(child: _buildHeader()),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 18.h)),
|
||||
SliverToBoxAdapter(child: _buildPlayerCard()),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 10.h)),
|
||||
SliverToBoxAdapter(child: _buildWalletShop()),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 10.h)),
|
||||
SliverToBoxAdapter(child: _buildCheckpoint()),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 10.h)),
|
||||
SliverToBoxAdapter(child: _buildGameMenu()),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 120.h)),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: Column(
|
||||
children: [
|
||||
const SizedBox(height: 5),
|
||||
mImgBg(imgName: ConstImg.mineTop),
|
||||
const SizedBox(height: 10),
|
||||
MineSectionView(title: 'PLAYER CARD', imgName: ConstImg.sectionBg2, paddingSpace: 16,
|
||||
child: _userInfo==null ? null : _showUserInfo(),
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
MineSectionView(title: 'GAME MENU', imgName: ConstImg.sectionBg1),
|
||||
],
|
||||
),
|
||||
)
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _showUserInfo() {
|
||||
String _name = _userInfo!.givingName!.isEmpty || _userInfo!.givingName==''
|
||||
? 'Visitor'
|
||||
: _userInfo!.givingName!;
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
_userInfo!.avator.isEmpty || _userInfo!.avator==''
|
||||
? HelpImg.asset(ConstImg.defAvatar, width: 60)
|
||||
: HelpImg.asset(ConstImg.defAvatar),
|
||||
const SizedBox(width: 10),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(height: 5),
|
||||
SkyText(text: _name, fontSize: 18, fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Row(
|
||||
children: [
|
||||
SkyText(text: _userInfo!.customerId,
|
||||
fontSize: 10,
|
||||
color: Colors.white.withAlpha(200),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
HelpImg.asset(ConstImg.copy, width: 12),
|
||||
],
|
||||
).addInkWell(onTap: () async {
|
||||
// copy
|
||||
if (_userInfo!.customerId.isEmpty) return;
|
||||
await Clipboard.setData(ClipboardData(text: _userInfo!.customerId));
|
||||
AppToast.showSuccess('ID is Copied!');
|
||||
}),
|
||||
const SizedBox(height: 10),
|
||||
],
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 15.w),
|
||||
|
||||
93
lib/pages/mine/widget/mine_section_view.dart
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:skywood/main/lib_file.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||
|
||||
class MineSectionView extends StatelessWidget {
|
||||
|
||||
const MineSectionView({
|
||||
super.key,
|
||||
this.width,
|
||||
this.height,
|
||||
required this.title,
|
||||
required this.imgName,
|
||||
this.type = 1, // 1,2
|
||||
this.paddingSpace = 5,
|
||||
this.child
|
||||
});
|
||||
|
||||
final String title;
|
||||
final double? width;
|
||||
final double? height;
|
||||
final String imgName;
|
||||
final int type;
|
||||
final double paddingSpace;
|
||||
final Widget? child;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
HelpImg.asset(imgName, width: width, height: height, fit: BoxFit.fill),
|
||||
Positioned(
|
||||
left: paddingSpace, right: paddingSpace, top: 8,
|
||||
child: Column(
|
||||
children: [
|
||||
_buildHeader(),
|
||||
const SizedBox(height: 10),
|
||||
child ?? SizedBox()
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
return Stack(
|
||||
children: [
|
||||
HelpImg.asset(ConstImg.sectionTopBg, width: width, height: 20.h),
|
||||
Positioned(
|
||||
left: 7, right: 7,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
HelpImg.asset(ConstImg.littleLeft, width: 7),
|
||||
const SizedBox(width: 2),
|
||||
SkyText(text: title, fontSize: 12, color: Colors.white)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: List.generate(3, (i) {
|
||||
return HelpImg.asset(ConstImg.littleRight, width: 5);
|
||||
}),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class mImgBg extends StatelessWidget {
|
||||
const mImgBg({super.key,
|
||||
this.width,
|
||||
this.height,
|
||||
this.fit = BoxFit.fill,
|
||||
required this.imgName,
|
||||
});
|
||||
|
||||
final double? width;
|
||||
final double? height;
|
||||
final String imgName;
|
||||
final BoxFit fit;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return HelpImg.asset(imgName, width: width, height: height, fit: fit);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:skywood/global/client/play_client.dart';
|
||||
import 'package:skywood/main/lib_file.dart';
|
||||
import 'package:skywood/pages/home/players/page/drama_detail_page.dart';
|
||||
import 'package:skywood/pages/widgets/pixel_widgets.dart';
|
||||
import 'package:skywood/utils/models/drama/skywood_drama.dart';
|
||||
import 'package:skywood/utils/tools/sky_time_tool.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_app_bar.dart';
|
||||
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
|
||||
|
||||
@ -25,12 +29,24 @@ class _ResultPageState extends State<ResultPage> {
|
||||
List<String> _searchHistory = [];
|
||||
final _searchController = TextEditingController();
|
||||
final _searchFocus = FocusNode();
|
||||
List<SkywoodDrama> _searchResult = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_searchController.text = widget.keyword;
|
||||
_search();
|
||||
}
|
||||
|
||||
void _search() {
|
||||
String keyword = _searchController.text;
|
||||
if (keyword.trim().isEmpty) return;
|
||||
PlayClient.search(keyword: keyword).then((valuer) {
|
||||
setState(() {
|
||||
_searchResult = valuer;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@ -56,9 +72,14 @@ class _ResultPageState extends State<ResultPage> {
|
||||
),
|
||||
),
|
||||
body: ListView.separated(
|
||||
itemBuilder: (context, index) => ResultItemView(),
|
||||
itemBuilder: (context, index) => ResultItemView(
|
||||
drama: _searchResult[index],
|
||||
onTap: () {
|
||||
HelpNav.to(DramaDetailPage(dramaBean: _searchResult[index]));
|
||||
},
|
||||
),
|
||||
separatorBuilder: (context, index) => SizedBox(height: 12),
|
||||
itemCount: 2
|
||||
itemCount: _searchResult.length
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -66,29 +87,41 @@ class _ResultPageState extends State<ResultPage> {
|
||||
|
||||
class ResultItemView extends StatelessWidget {
|
||||
|
||||
const ResultItemView({super.key});
|
||||
const ResultItemView({super.key,
|
||||
required this.drama,
|
||||
this.onTap
|
||||
});
|
||||
|
||||
final SkywoodDrama drama;
|
||||
final Function()? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String tag = 'Satisfying';
|
||||
if (drama.categoryList!.isNotEmpty) {
|
||||
tag = drama.categoryList![0].name;
|
||||
}
|
||||
return Container(
|
||||
height: 140.h,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Row(
|
||||
children: [
|
||||
PosterImage(width: 105.w, height: 140.h),
|
||||
HelpImg.networkRadius(drama.imageUrl, width: 105.w, height: 140.h,
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
SkyText(text: 'Love in the City of Twilight',
|
||||
SkyText(text: drama.name,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: CommonColor.black,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_tagView(),
|
||||
_tagView(tag),
|
||||
const SizedBox(height: 12),
|
||||
SkyText(text: 'Haunted by fading memories, a man navigates a labyrinth of dreams and reality ds dsajkj',
|
||||
SkyText(text: '${drama.description}',
|
||||
fontSize: 11,
|
||||
color: CommonColor.withValue('4D4047'),
|
||||
maxLines: 2,
|
||||
@ -98,14 +131,14 @@ class ResultItemView extends StatelessWidget {
|
||||
children: [
|
||||
_tipView(true, text: '10.0'),
|
||||
const SizedBox(width: 15),
|
||||
_tipView(false, text: '10.2k'),
|
||||
_tipView(false, text: SkyTimeTool.formatNum(drama.watchTotal, unit: 'k')),
|
||||
],
|
||||
)
|
||||
],
|
||||
))
|
||||
],
|
||||
),
|
||||
);
|
||||
).addInkWell(onTap: onTap);
|
||||
}
|
||||
|
||||
Widget _tipView(bool isStar, {required String text}) {
|
||||
@ -119,7 +152,7 @@ class ResultItemView extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _tagView() {
|
||||
Widget _tagView(String tag) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(top: 3, left: 6, right: 6, bottom: 4),
|
||||
decoration: ShapeDecoration(
|
||||
@ -139,7 +172,7 @@ class ResultItemView extends StatelessWidget {
|
||||
spacing: 10,
|
||||
children: [
|
||||
Text(
|
||||
'Satisfying',
|
||||
tag,
|
||||
style: TextStyle(
|
||||
color: const Color(0xFF040408) /* 主题黑 */,
|
||||
fontSize: 10,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:skywood/global/client/play_client.dart';
|
||||
import 'package:skywood/main/lib_file.dart';
|
||||
import 'package:skywood/pages/home/players/page/drama_detail_page.dart';
|
||||
import 'package:skywood/pages/search/page/result_page.dart';
|
||||
import 'package:skywood/pages/search/widget/search_util.dart';
|
||||
import 'package:skywood/pages/search/widget/search_view.dart';
|
||||
@ -174,39 +175,23 @@ class _MainRootState extends State<SearchPage> {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: group.map((drama) {
|
||||
return _TrendingItem(drama: drama);
|
||||
return _TrendingItem(drama: drama, onTap: () {
|
||||
HelpNav.to(DramaDetailPage(dramaBean: drama));
|
||||
});
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
// Widget _buildTrendingList() {
|
||||
// return Container(
|
||||
// height: 114 * 4, width: AppScreen.screenWidth,
|
||||
// color: Colors.green,
|
||||
// child: GridView.builder(
|
||||
// padding: EdgeInsets.only(left: 15.w),
|
||||
// scrollDirection: Axis.horizontal,
|
||||
// gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
// crossAxisCount: 4, // 上下4行
|
||||
// crossAxisSpacing: 10, // 行间距(垂直方向)
|
||||
// mainAxisSpacing: 10, // 列间距(水平方向)
|
||||
// childAspectRatio: 236 / 104.0, // 宽高比(宽度/高度)
|
||||
// ),
|
||||
// itemCount: _searchHots.length,
|
||||
// itemBuilder: (context, index) {
|
||||
// final drama = _searchHots[index];
|
||||
// return Container(width: 236, height: 104, color: index%2==0 ? Colors.orangeAccent : Colors.redAccent,);
|
||||
// return _TrendingItem(drama: drama);
|
||||
// }
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
class _TrendingItem extends StatelessWidget {
|
||||
const _TrendingItem({required this.drama});
|
||||
const _TrendingItem({required this.drama,
|
||||
this.onTap
|
||||
});
|
||||
|
||||
final SkywoodDrama drama;
|
||||
final Function()? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -261,6 +246,6 @@ class _TrendingItem extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
).addInkWell(onTap: onTap);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import '../model_parser.dart';
|
||||
|
||||
class GlimzoUser {
|
||||
class SkywoodUser {
|
||||
final String customerId;
|
||||
final bool isTourist;
|
||||
final String userLevel;
|
||||
@ -8,7 +8,7 @@ class GlimzoUser {
|
||||
final String? familyName;
|
||||
final String? givingName;
|
||||
|
||||
const GlimzoUser({
|
||||
const SkywoodUser({
|
||||
required this.customerId,
|
||||
required this.isTourist,
|
||||
required this.userLevel,
|
||||
@ -17,8 +17,8 @@ class GlimzoUser {
|
||||
this.givingName,
|
||||
});
|
||||
|
||||
factory GlimzoUser.fromJson(Map<String, dynamic> json) {
|
||||
return GlimzoUser(
|
||||
factory SkywoodUser.fromJson(Map<String, dynamic> json) {
|
||||
return SkywoodUser(
|
||||
customerId: parseString(json['customer_id']),
|
||||
isTourist: parseBool(json['is_tourist']),
|
||||
userLevel: parseString(json['user_level']),
|
||||
@ -39,7 +39,7 @@ class GlimzoUser {
|
||||
};
|
||||
}
|
||||
|
||||
GlimzoUser copyWith({
|
||||
SkywoodUser copyWith({
|
||||
String? customerId,
|
||||
bool? isTourist,
|
||||
String? userLevel,
|
||||
@ -47,7 +47,7 @@ class GlimzoUser {
|
||||
String? familyName,
|
||||
String? givingName,
|
||||
}) {
|
||||
return GlimzoUser(
|
||||
return SkywoodUser(
|
||||
customerId: customerId ?? this.customerId,
|
||||
isTourist: isTourist ?? this.isTourist,
|
||||
userLevel: userLevel ?? this.userLevel,
|
||||
@ -61,6 +61,26 @@ class ConstImg {
|
||||
static const String playLt = "assets/pics/player_lt.png";
|
||||
static const String liked = "assets/pics/liked.png";
|
||||
static const String unlike = "assets/pics/unlike.png";
|
||||
static const String espBg = "assets/pics/esp_bg.png";
|
||||
static const String cateSelLeft = "assets/pics/cate_sel_left.png";
|
||||
static const String cateSelRight = "assets/pics/cate_sel_right.png";
|
||||
|
||||
static const String playTag = "assets/pics/play_tag.png";
|
||||
static const String rank1 = "assets/pics/rank1.png";
|
||||
static const String rank2 = "assets/pics/rank2.png";
|
||||
static const String rank3 = "assets/pics/rank3.png";
|
||||
|
||||
/// mine
|
||||
static const String copy = "assets/mine/copy.png";
|
||||
static const String littleLeft = "assets/mine/little_left.png";
|
||||
static const String littleRight = "assets/mine/little_right.png";
|
||||
static const String mineTop = "assets/mine/mine_top.png";
|
||||
static const String sectionTopBg = "assets/mine/section_top_bg.png";
|
||||
static const String sectionBg1 = "assets/mine/section_bg1.png";
|
||||
static const String sectionBg2 = "assets/mine/section_bg2.png";
|
||||
static const String sectionBg3 = "assets/mine/section_bg3.png";
|
||||
static const String defAvatar = "assets/mine/def_avatar.png";
|
||||
|
||||
|
||||
|
||||
// nodata
|
||||
|
||||
@ -16,7 +16,7 @@ class SkyTimeTool {
|
||||
num value, {
|
||||
int decimalDigits = 1,
|
||||
String separator = ',',
|
||||
String unit = 'K',
|
||||
String unit = 'k',
|
||||
}) {
|
||||
String removeTrailingZeros(String str) {
|
||||
if (str.contains('.')) {
|
||||
|
||||