diff --git a/assets/mine/copy.png b/assets/mine/copy.png new file mode 100644 index 0000000..c44e0f3 Binary files /dev/null and b/assets/mine/copy.png differ diff --git a/assets/mine/def_avatar.png b/assets/mine/def_avatar.png new file mode 100644 index 0000000..1c832f0 Binary files /dev/null and b/assets/mine/def_avatar.png differ diff --git a/assets/mine/little_left.png b/assets/mine/little_left.png new file mode 100644 index 0000000..b484a17 Binary files /dev/null and b/assets/mine/little_left.png differ diff --git a/assets/mine/little_right.png b/assets/mine/little_right.png new file mode 100644 index 0000000..8c21635 Binary files /dev/null and b/assets/mine/little_right.png differ diff --git a/assets/mine/mine_top.png b/assets/mine/mine_top.png new file mode 100644 index 0000000..446ccc6 Binary files /dev/null and b/assets/mine/mine_top.png differ diff --git a/assets/mine/section_bg1.png b/assets/mine/section_bg1.png new file mode 100644 index 0000000..63cf356 Binary files /dev/null and b/assets/mine/section_bg1.png differ diff --git a/assets/mine/section_bg2.png b/assets/mine/section_bg2.png new file mode 100644 index 0000000..d6b0f3a Binary files /dev/null and b/assets/mine/section_bg2.png differ diff --git a/assets/mine/section_bg3.png b/assets/mine/section_bg3.png new file mode 100644 index 0000000..564f88d Binary files /dev/null and b/assets/mine/section_bg3.png differ diff --git a/assets/mine/section_top_bg.png b/assets/mine/section_top_bg.png new file mode 100644 index 0000000..e832484 Binary files /dev/null and b/assets/mine/section_top_bg.png differ diff --git a/assets/pics/cate_sel_left.png b/assets/pics/cate_sel_left.png new file mode 100644 index 0000000..e87e1fc Binary files /dev/null and b/assets/pics/cate_sel_left.png differ diff --git a/assets/pics/cate_sel_right.png b/assets/pics/cate_sel_right.png new file mode 100644 index 0000000..34e5aa9 Binary files /dev/null and b/assets/pics/cate_sel_right.png differ diff --git a/assets/pics/esp_bg.png b/assets/pics/esp_bg.png new file mode 100644 index 0000000..25481cc Binary files /dev/null and b/assets/pics/esp_bg.png differ diff --git a/assets/pics/play_tag.png b/assets/pics/play_tag.png new file mode 100644 index 0000000..c7fc7b1 Binary files /dev/null and b/assets/pics/play_tag.png differ diff --git a/assets/pics/rank1.png b/assets/pics/rank1.png new file mode 100644 index 0000000..27aaaa7 Binary files /dev/null and b/assets/pics/rank1.png differ diff --git a/assets/pics/rank2.png b/assets/pics/rank2.png new file mode 100644 index 0000000..365b239 Binary files /dev/null and b/assets/pics/rank2.png differ diff --git a/assets/pics/rank3.png b/assets/pics/rank3.png new file mode 100644 index 0000000..4e806bc Binary files /dev/null and b/assets/pics/rank3.png differ diff --git a/lib/global/client/play_client.dart b/lib/global/client/play_client.dart index bed9fd6..bc3cc2d 100644 --- a/lib/global/client/play_client.dart +++ b/lib/global/client/play_client.dart @@ -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> getHomeData() { return AppRequest.get(ApiStringPlay.getHomeModules).then((value) { if (value.isSuccess) { @@ -42,6 +40,20 @@ class PlayClient { }); } + static Future> getCategoriesDetail({ + required int categoryId, + int page = 1, + int pageSize = 20, + }) { + return AppRequest.fetchPage( + ApiStringPlay.getCategoriesDetail, + page: page, + pageSize: pageSize, + extraParams: {'category_id': categoryId}, + fromJson: (json) => SkywoodDrama.fromJson(json), + ); + } + // getSearchHots static Future> getSearchHots() { return AppRequest.get(ApiStringPlay.getSearchHots).then((value) { @@ -54,29 +66,53 @@ class PlayClient { }); } + // search + static Future> 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 getCollections({ - int page = 1, int pageSize = 10 + static Future> getCollections({ + int page = 1, + int pageSize = 10, }) { Map 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 getHistory({ - int page = 1, int pageSize = 10 + static Future> getHistory({ + int page = 1, + int pageSize = 10, }) { Map 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 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 collect({ + required int shortPlayId, + required int videoId, + }) { + Map 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 cancelCollect({required int shortPlayId}) { + Map 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 _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 data) { @@ -110,4 +175,4 @@ class PlayClient { List? listData = map['list']; return listData ?? []; } -} \ No newline at end of file +} diff --git a/lib/global/client/user_client.dart b/lib/global/client/user_client.dart index a1712ea..edd0d87 100644 --- a/lib/global/client/user_client.dart +++ b/lib/global/client/user_client.dart @@ -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 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; + }); + } } \ No newline at end of file diff --git a/lib/global/request/api_const.dart b/lib/global/request/api_const.dart index 75eebaa..ff3ca32 100644 --- a/lib/global/request/api_const.dart +++ b/lib/global/request/api_const.dart @@ -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 diff --git a/lib/main/main_root.dart b/lib/main/main_root.dart index 419f1dc..9124085 100644 --- a/lib/main/main_root.dart +++ b/lib/main/main_root.dart @@ -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 { final List _pages = const [ - HomePage(), - SearchPage(), + AppKeepAlive(child: HomePage()), + AppKeepAlive(child: SearchPage()), LikePage(), - MinePage(), + AppKeepAlive(child: MinePage()), ]; final _pageController = PageController(); diff --git a/lib/pages/home/cate_page.dart b/lib/pages/home/cate_page.dart new file mode 100644 index 0000000..b9b600d --- /dev/null +++ b/lib/pages/home/cate_page.dart @@ -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 createState() => _CatePageState(); +} + +class _CatePageState extends State { + final List _categories = []; + final List _dramas = []; + int _selectedIndex = 0; + int? _loadingCategoryId; + bool _loadingCategories = true; + bool _loadingDramas = false; + + @override + void initState() { + super.initState(); + _loadCategories(); + } + + Future _loadCategories() async { + List 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 _loadCategoryDetail(int categoryId) async { + _loadingCategoryId = categoryId; + setState(() { + _loadingDramas = true; + _dramas.clear(); + }); + + List 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), + ], + ); + } +} diff --git a/lib/pages/home/players/page/drama_detail_page.dart b/lib/pages/home/players/page/drama_detail_page.dart index abe59f7..11b6925 100644 --- a/lib/pages/home/players/page/drama_detail_page.dart +++ b/lib/pages/home/players/page/drama_detail_page.dart @@ -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 { + 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 { 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,) + ], + ), + ); + } + } \ No newline at end of file diff --git a/lib/pages/home/players/page/drama_esp_page.dart b/lib/pages/home/players/page/drama_esp_page.dart new file mode 100644 index 0000000..f7a0f81 --- /dev/null +++ b/lib/pages/home/players/page/drama_esp_page.dart @@ -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 createState() { + return _DramaEspPageState(); + } +} + +class _DramaEspPageState extends State { + late int _selectedEpisode; + + SkywoodDrama? get _drama => + widget.detail.shortPlayInfo ?? widget.detail.videoInfo; + + List 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; + } +} diff --git a/lib/pages/home/players/widget/player_bottom_widget.dart b/lib/pages/home/players/widget/player_bottom_widget.dart index 8a06217..454a7cd 100644 --- a/lib/pages/home/players/widget/player_bottom_widget.dart +++ b/lib/pages/home/players/widget/player_bottom_widget.dart @@ -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 createState() { @@ -92,4 +98,10 @@ class _PlayerBottomWidgetState extends State { ), ); } + + 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'; + } } \ No newline at end of file diff --git a/lib/pages/home/players/widget/sky_player_view.dart b/lib/pages/home/players/widget/sky_player_view.dart index e69de29..c6003c4 100644 --- a/lib/pages/home/players/widget/sky_player_view.dart +++ b/lib/pages/home/players/widget/sky_player_view.dart @@ -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 createState() => _VideoPlayerWidgetState(); +} + +class _VideoPlayerWidgetState extends State { + 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 _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; + } +} diff --git a/lib/pages/home/widgets/daily_quest_widget.dart b/lib/pages/home/widgets/daily_quest_widget.dart index 01dcc5f..4120753 100644 --- a/lib/pages/home/widgets/daily_quest_widget.dart +++ b/lib/pages/home/widgets/daily_quest_widget.dart @@ -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), + ); }) ), ); diff --git a/lib/pages/home/widgets/home_cate_widget.dart b/lib/pages/home/widgets/home_cate_widget.dart index 7100bd9..f9a5472 100644 --- a/lib/pages/home/widgets/home_cate_widget.dart +++ b/lib/pages/home/widgets/home_cate_widget.dart @@ -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) { diff --git a/lib/pages/home/widgets/hot_cartridge_widget.dart b/lib/pages/home/widgets/hot_cartridge_widget.dart index 17b09b8..260ba36 100644 --- a/lib/pages/home/widgets/hot_cartridge_widget.dart +++ b/lib/pages/home/widgets/hot_cartridge_widget.dart @@ -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)); + }); } } \ No newline at end of file diff --git a/lib/pages/home/widgets/rank_list_widget.dart b/lib/pages/home/widgets/rank_list_widget.dart index ead2583..d3d9f05 100644 --- a/lib/pages/home/widgets/rank_list_widget.dart +++ b/lib/pages/home/widgets/rank_list_widget.dart @@ -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'); } diff --git a/lib/pages/like/pages/collection_page.dart b/lib/pages/like/pages/collection_page.dart index 311cdd6..829b3ac 100644 --- a/lib/pages/like/pages/collection_page.dart +++ b/lib/pages/like/pages/collection_page.dart @@ -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 { + List _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 { 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)); + },); } } \ No newline at end of file diff --git a/lib/pages/mine/mine_page.dart b/lib/pages/mine/mine_page.dart index 0daef54..d0a966b 100644 --- a/lib/pages/mine/mine_page.dart +++ b/lib/pages/mine/mine_page.dart @@ -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 { + + 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), diff --git a/lib/pages/mine/widget/mine_section_view.dart b/lib/pages/mine/widget/mine_section_view.dart new file mode 100644 index 0000000..4eeedcf --- /dev/null +++ b/lib/pages/mine/widget/mine_section_view.dart @@ -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); + } +} \ No newline at end of file diff --git a/lib/pages/search/page/result_page.dart b/lib/pages/search/page/result_page.dart index d4993c8..7a360b6 100644 --- a/lib/pages/search/page/result_page.dart +++ b/lib/pages/search/page/result_page.dart @@ -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 { List _searchHistory = []; final _searchController = TextEditingController(); final _searchFocus = FocusNode(); + List _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 { ), ), 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 { 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, diff --git a/lib/pages/search/page/search_page.dart b/lib/pages/search/page/search_page.dart index f623878..29ec4b8 100644 --- a/lib/pages/search/page/search_page.dart +++ b/lib/pages/search/page/search_page.dart @@ -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 { 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); } } diff --git a/lib/utils/models/user/glimzo_user.dart b/lib/utils/models/user/skywood_user.dart similarity index 88% rename from lib/utils/models/user/glimzo_user.dart rename to lib/utils/models/user/skywood_user.dart index 4b72edc..e60a506 100644 --- a/lib/utils/models/user/glimzo_user.dart +++ b/lib/utils/models/user/skywood_user.dart @@ -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 json) { - return GlimzoUser( + factory SkywoodUser.fromJson(Map 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, diff --git a/lib/utils/tools/help_img.dart b/lib/utils/tools/help_img.dart index 721610f..dc4f988 100644 --- a/lib/utils/tools/help_img.dart +++ b/lib/utils/tools/help_img.dart @@ -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 diff --git a/lib/utils/tools/sky_time_tool.dart b/lib/utils/tools/sky_time_tool.dart index bc2f74c..ff20710 100644 --- a/lib/utils/tools/sky_time_tool.dart +++ b/lib/utils/tools/sky_time_tool.dart @@ -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('.')) {