From b5bce6edcfbfbd80c9d914094f2c649951c44ca7 Mon Sep 17 00:00:00 2001 From: phoenix--zhang <471155679@qq.com> Date: Mon, 6 Jul 2026 20:56:12 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=90=9C=E7=B4=A2=E9=A1=B5=E7=9A=84?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=EF=BC=8C=E7=BC=BA=E7=9C=81=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/imgs/home_eyes.png | Bin 0 -> 7711 bytes assets/other/empty_img.png | Bin 0 -> 53373 bytes lib/common/api/api_string.dart | 2 + lib/common/api/ny_network_intercept.dart | 4 +- lib/common/api/video_request.dart | 19 +- lib/common/help/help_image.dart | 2 + lib/pages/first/pages/first_page.dart | 12 +- lib/pages/first/pages/search_page.dart | 124 ++++- lib/pages/first/pages/search_result_page.dart | 83 +++ lib/pages/first/widgets/home_first_view.dart | 12 +- lib/pages/first/widgets/home_rank_widget.dart | 135 ++++- .../widgets/home_remaining_sections.dart | 70 +-- lib/pages/first/widgets/search_view.dart | 23 +- lib/pages/play/pages/play_page.dart | 116 ++++- lib/pages/play/widgets/player_view.dart | 477 ++++++++++++++++++ lib/pages/widgets/ny_empty_view.dart | 81 +++ lib/pages/widgets/ny_tag_widget.dart | 4 +- lib/root/root_page.dart | 108 ++-- 18 files changed, 1101 insertions(+), 171 deletions(-) create mode 100644 assets/imgs/home_eyes.png create mode 100644 assets/other/empty_img.png create mode 100644 lib/pages/play/widgets/player_view.dart create mode 100644 lib/pages/widgets/ny_empty_view.dart diff --git a/assets/imgs/home_eyes.png b/assets/imgs/home_eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..e4d21d13ddf3258a682be2a47829de2e20b1dee3 GIT binary patch literal 7711 zcmV+)9^m1LP)9Vfg%En z#AgHzL?S)_S4}joSz|QDZ$ZCkqRF~()hs4!jG9Cv@e!hsC@U;}nui!yU3C*#1Yrn@ z!(-^4>FKJvch5QZR?i4CGCecEJo-$^epO9R?qBDe|NQ4v0Z_D}6|HDRD_YMq0Y&Sq zNoZb2dNjCc_t@oD1gzs$>3QB*W6Ve?WeT_VaeKY7v2mmO+ye_*1VuZbHUqb{xS6uD zGIQ|32kS!)IYi_0*sBZz`A#yKJk{;5zZA{k54hXNGakYR^V_j0_Jq=$*R*WiGz>Vl!^sxGX*&2u15M z^W3AUtE-DBAGH^-hK2^SapT54fQq*@Yu5077hG^bEp)(&^+6k!N~KQ1PzF_1Rp@B# zUnbfYK><3pHCw%UH9y{S&pkExa1#`0MZj7&%31S|KmK@DDGCK_`}XZ^et!4ecf*by zJD`96{xEFVu-l+OD+1QK5i2}l+O%na;j5qn$+LzI9SSy$?P(h}Y=H6O$HT)9KMd>F zucx;jf}(ZaaN$3A?6Jo}A`ykKLt>L#wrpuLhqbW{Y46`v^su%qf%+g6YUE*q2M>nI z%E~tLSSxm4dw1-MfC<=BBBFI5k=ASRSvH+civkw?V|K;mm$w-fTd{M%N>_Wez6O|& z+hpACLm>W$+h36+{2A+|AE8Kl628ycpWFAtci(-NlnyA=P`-k1-gFb7y3yW4;=gOJ zR`_4jGmRGHR@{DrVQ#^RC!RR|3+JC-bJp2s4>oF$W!VP-}W(UTbXenj%4;B!U#`39y}CzVb?TDFUnzrJd}R zKmEyIh%{%Nb(SeaPizge;lqc=-RM2QyXmHz42EHPUIl#+Fs605D9;>u5IlFhLbkKhc_P+qR9Lw{;xS ze0buCC*b_^&j(y+=%@4_R0>*rBou9a{?J2K zR434mKKf|5@x~iFq=M{j^gCAl*GiJW;(^moJj;3;V8r5188Ko6|DP_z18Cohii$Qr zfBp5>VZnk0aZ7|6klfemtd}9tfQ1YHS(H}#zk}vc?>=MXRgWN>i`qMmwma!SmA%a( zclYh;tFP`f0g3Y$cDO$c?I0Yzu}JfgZ9zxv{PWIhx61y&0}mA3;B09Wy1)G8E2&Gp z5y5*O_7V=sdL3Z6*mdb$yE@bsAp&sYH^15L{c(}u`RAYSP%fjtZ|&N5&Em!Pld69f zSi;oz07LuB#!Z_#lv~KN4nO>Gm@;KbyN`L~kw^AQSavr8nC3o?Kkj&e-nAHOu{WU| zVD(0$6L2D3Rni&lkCcekis!br*|TSvNLv+y@={y%AIVx|uf6t~sj8}?V}IM?@9vXE zZq*MrQ(s@-;kF1F=BHNWsogivQ>LQY=c`rDIP-L~?!EuZ%T3fBh}$`28#Zh(bLPy6 zkG&g;mbabDmMzmAVTvt5yZPpu+nnHyfuzfizW!p(`&Tcm**x?!$!z?2&A~P1lv5@m zh-aAb=g0UYNT+u}YJdna zVe+^z1xxFj+8}WFQX^O}`!Ay**8uhp&%-oasqX@*^|)GNXTj$BOkH^Iz4vx1 zEVeZAFq)lj>1nmKn_KZx^M%n3EiCHk#Yh5BMwfkoCU09){!_xE@@e+|!R&6~zk z8&_+yiiTLx1> zN7n!)3noB5c;UI;DgedZ2gQGGrAlw3Xxm3Y~kk>gCL+Vr~$tgH!Qwt18uIn zWmMOKMb@L|2QFO>lQBA7%^`{jHP=AG&SG0Rark8T@R_?de7eL>ib<0ubxBSmqcD5J zjo*Ox>fXqWzc8_#Ss~LwQEP;pd&{WWO|TEF#{xEQsdp;+%vBaE!zc-&XT}h%jtds! zr-U9jLe$^?tred-=9qCXdi3Z{gGPjd`dH_jeGbgHqhfRagJmGdfemadN+5j|_>IJ_DyU6>1Jz;6{&}MFc)-4vh9S%6FeW+0>nIxp-&BjG3@(`7-v7 z&VWX3k?(xxcJPuy4?h{w#(q_8iPUp%K4Q~81BE5yTaKvPr(p5-Ll3e`o`91z$jb!RqIj}NO@I(QtSx{r^or66p&;F7iXQnr+}>La z1D{)H{%*?DDX`-C75satwrUUcr+sQMHH{uU28K>-*nakRlAG~+H+$NjciXsq4;ERE z35j!;cqd^9yaYH55)%aMXGjcu((r4fW`e_`{T#ue&}c6-Al^oFA8Xq~CqdnXkHhL$ z-d@!}3dZ95|DC3MSjXF)#opO!(AXOzI{1t!mL0xv|-|b(p z$htLPUwX(t-gv>62$UEQ`oXZr-bBi(VX)B4XdjoqV$gs$(VWv1<~U#=h20Oqk4OMF z0tpHIZV_d{tFINQrZjkd8JK=GCQ9|gpo*U<*4B}F`cM*opU*aDf$DNT+>I=QQv z>4)=}u@SIE$AdnTb`O4Gz#il(0R+i#2TPj#{`}q2)F%vFdDr*@2Nqek18mlhVJa@v z)Oa$`UGi@sV_?|jgl%2eH6^!UVkivQAA6K^OKvRecBD3soEtu5g~G~MMz~!>%sZ^s zgg?7*p}z8i6X9Jrz}Bq*`@)Z5vXtHw6b^)4V$Em2asOkKcr~?ky|{RG%S=ZwDU{C1 z>^lx4@$vgT*ha8(B8wDxk})mbq@%S+d!hO2zfC;wV3Bnjz|LFjO~TbY)%klT2`ybmT~az7=1`cn-I&BMDQzB4=(vfw^377+f>#JLEZ^U+iVK#T&1O|YKX z94NsKK5^2E9oRUzpK+xSd*|X)^0R~8!VWlK=iTF-jB7ZN34=ZR*!5WhdutAGV@-EF~Nt~@>Js6rbU=FM7GB!lg@1xISZvva_ z+@(H-{*HhB@l<#ox}hB~z|Q@Fe*#K;C-BNi_wQi{4r`p7;=u_HXTthh+z@dbFJ|?k zMgAg3vt-4>5$8ZFn#!0p8evN|kv|Wl^-TJC;6F}-WzbFSfB<&R_xA(u znYa1lFytM>fZ)Pu?l#7R4f35i*-RfC<3q1t7qqVxfo5xFhf9nafu~G-5Hy`4VFyEExH~NTV5~-P$lF zm@l#u8HctlgJTT9?qhSA9cj;}I{VZX)tuXG37~)d&^f23}fSqxBVl+C^7@N~wR|@RH#x7;F5=P`J+q`&giHK&G4MKYViH${xPEG0T*o*7oB*qY z?dTh;NEwK^9}^5_^lw8zIal~XFH z^fH#nlk6e#9WP7MEv>ZrpoSJ2mC=9x<)!dA^b+e5z@}aYhvEVaqp>h0T~I=BjEq=) z;E^L9Hj9kXl0g)%76ZrOHD@r>T#yOr2t&T-1?;B?nj*(@Vb!%wtc+y`1o$HxZuM)A z&xc<@FS1SpHt`?%rdEeC1y`PkqL4W`J+iY%A{~oJGE6>55;Z6_V$#xo@Sp{LTwewqoNtOKw4p!f)xzGvgYywH55qEsqCt9^9;I0}qn#JZxNeJc zniZ2j+>tatTQv`|&==He!g71%y12X?P4*#-jiX znzX=LE+Xx`C^{d$@EAO4AkQ8(*J8T4KsL4yL&Q+7P&SADJbf<;ykU?GJ6gK{JL$^uK`O|V z(-m>7J5VDrfkXhEDU`N^)ewLTFrc9nfz_5!dC;hp?4d(y7Ssha*I*<)Ss}y{48B6h zB}7h6wr}Ecqja6($Py4cteBQj^y_D@gI&-^Y_EVFc`*zuiDY>oJxuH&`c(;dB_8N7 zXQYqbq|xKF;;9Q{aUANHtsy!#r-kGuxDP(}oNjAe0}Bgjy3|YAg4s4EJvVwm^_Nvf z40#tNFJzx0!r!A5`L;=;r$4*&hQ0$9*(U-v?o#NVlBx0tlT{?L$R(0OCz=47&?8hr zY9fT_O-(W|zE=61mWu)udwqP3K;bh6PS~-E_!|i{s-+a=IU`x=pzg9|=Q7_vvr|%% zg=D_b7G3_M!(F8nX8BWJgEaIpYZI`M7gklKj3|wAjYdU2I)o2C%X%K3=^=vPadT0X zP-0S&Mm?1eG+zV0R3auK#2y8E8ou^{*`ua!4XIv}PUm%F7aT!tuHurxFv!*`m9#TA z8ZwQ6d2ZP^`W{$htpaw$=b=LQUWshXfR`*4QBoK`XvDnbi6)(eB>qq?3+bBUKybf{ zTOUy~Xp*PMFs~tVl#y%9Q{AL&OGLNuG!wH&>W>lkBfz4FSt7M!I3n7~&<351R{ZC; zdOsU%%k}`Q=A4?+PYn_!UI3z^JOL&EXv{W1GLaIR?zkC-&_u;C4biB`G$D)g6LfpJ z99LyJ!upIyQ zGbT|vB%>P8iAu#b2%=Jx&^v`l0R%Z@h9-m2FM*;fhP?!_HNiQJiJxgg^$AIXmk>=N zhrlYn>_8(K0oxO^IZ4<{T{+L$yQ4JE)(8+S!(%*ch-4&8dV2Np+o1u9#wkNg7T`6& zR;1Lxeks z98krkOoFs~{KU#wRI8DDX*YJ3pW#rhu}smDiaTo0@QEOzOhW(ug>VfLF zW*gYLReyTOCees}NJy-QYW&%h#saKF>d5aW24V)q{x zUqP_?go4ptYWAzUa_s0u8oKj#97+c9dg)8ek>D5{zW2gpf@cp($eJ z$^jCp48p8Ial;r72_x26SP0v@g8D?wJuBej!fVC-EzErp-8cxnO>iHd8pr&;K~<7U z1L7d4j3EB-`szi+FxZ;qZw^MFJ-*~|U0q#m%FD+Xj6+dHZkSGHL8o)ox-r;+8#+l9 z)?}#t=5}VZ7d7Y{1~t03MSzbAHxB^{bdwJ8xQK4ng{-`>30s}`J~B=0NC)ISn&f+B zxW|dlN!vx;BLJQ7VeP6SSZ$iv{S9L;&6meseyq%He^pe%P?bq;l2Nu=lqVwevTZWu zSIX@e8mr2Ust?mh?Lj7F3X@%%43Crm(Q^`p+hVAS&`pLUDp!1E5no-T z0t|;fheHzt>t0?AA41XEv^{RW7l+3Y+ehf+05DtANnNFeMaitM)3r*MlvSap)$0Km z@|r@S{Uk=eWSI_SW0Z?Zb%0?rU{6c(qf|}EEOHTfT11|Pn?g?`?;=mbgvGbU^Ao|^ zB3OI3R)=uxl?O)8nl&5N9kK*c8%N5fatvyKpdlk{Dyss>AOZAum|zEjj%+^RmC16? zm~_|}C6F1?zgwpgNeqBV?T1K2Fx;tR0-0n^ODxJst*upT9ybG_QFsh;(km(7^5Q+|tx&Xf*ews)oa44l+RkDoU}`!a_L2y#l222`aEj^T3`O#!W7&k#CZ) zyY;0X=89kyV0-g0HYOsoMc&hPY*{Na$-ziOK0r?ck&^vY%EthQp2l@rKV4A){Sg$yV1XvvvlHdCMPnA35!*p8e{*QGH4VB9Nr>8s z>P1PlY1BKS#xkjM*hoQCOfDkIkHiB>M=omAK}1wP#nhfCztT+~6~XG5wadd|ixrV| z>(O`sW5~2ETzcHuzyD0RsY{Vg&k0| z4p~8fky+4$TsPoY439hIn`^Ri@IfSFZI;+RlfLPvu+9<@hQ`3#B?Ht5<2o&uY)qqP z?S!Ir*b1JlcNZHY<$o0rG)VRbs2jLe3{2EOdV2tR(-2)CN0i9iFX!>um#9j93BNc6#QZruCvy??RSXVeg2H{j_8<1@!^;(PTp8L^`u( zNl|X<3TqeXXw7KGbJ3VFW)^CX{XPWUf5=ECBFO<;skmnCF@xG;BHWdE{#*yjHQG9L_sRJcPlVPovDA=%u@2YdId`~J30U(iROEn?6@ z7``b5LH|JXuiOS$ONTE%2stQP_q0We7Rgz&K%vU-!{pxguh;fUBhX+`$Ma&*iq`Aw Z{{iQ3RN1_doxuPA002ovPDHLkV1lvRyC47n literal 0 HcmV?d00001 diff --git a/assets/other/empty_img.png b/assets/other/empty_img.png new file mode 100644 index 0000000000000000000000000000000000000000..a5273542e77ccbc748c7c60e3f83a71ef53a46e2 GIT binary patch literal 53373 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

KHkcv_T0W-&K%{_+0YGs?H?c{)ii&ydiw1BiQix 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 _addRequestHeaders(RequestOptions options) async { if (!kReleaseMode) { options.headers['security'] = 'false'; diff --git a/lib/common/api/video_request.dart b/lib/common/api/video_request.dart index 3ed62cd..e37a2af 100644 --- a/lib/common/api/video_request.dart +++ b/lib/common/api/video_request.dart @@ -36,13 +36,28 @@ class VideoRequest { if (list != null) { // id 28 不显示,删除 categoryList = list.map((e) => NyCategoryModel.fromJson(e)).toList(); - categoryList.removeWhere((element) => element.id == 28); categoryList.removeWhere((element) => element.name.length >= 10); } return categoryList; }); } + // getRecommandsList + static Future> getRecommandsList({ + int page = 1, + int pageSize = 10, + }) { + Map params = _setPage(page: page, pageSize: pageSize); + return ApiRequest.get(ApiStringVideo.getRecommandsList, queryParameters: params).then((value) { + if (value.isSuccess) { + // HelpLog.print('getRecommandsList: ${value.data}'); + List list = parseDataForList(value.data); + return list.map((e) => NyDramaModel.fromJson(e)).toList(); + } + return []; + }); + } + static Future> getCategoriesDetail({ required int categoryId, int page = 1, @@ -58,7 +73,7 @@ class VideoRequest { } // getSearchHots - static Future> getSearchHots() { + static Future> getSearchTrending() { return ApiRequest.get(ApiStringVideo.getSearchHots).then((value) { if (value.isSuccess) { HelpLog.print('getSearchHots: ${value.data}'); diff --git a/lib/common/help/help_image.dart b/lib/common/help/help_image.dart index ba57710..8aa7bf1 100644 --- a/lib/common/help/help_image.dart +++ b/lib/common/help/help_image.dart @@ -36,6 +36,7 @@ class ConstImage { static const String btnBgRad2 = "assets/imgs/btn_bg_rad2.png"; // remove static const String remove = "assets/imgs/remove.png"; + static const String homeEyes = "assets/imgs/home_eyes.png"; /// other static const String mineBg = "assets/other/mine_bg.png"; @@ -49,6 +50,7 @@ class ConstImage { // pricary web_site static const String primary = "assets/other/pricary.png"; static const String webSite = "assets/other/web_site.png"; + static const String emptyImg = "assets/other/empty_img.png"; } diff --git a/lib/pages/first/pages/first_page.dart b/lib/pages/first/pages/first_page.dart index 67bf623..8d831bd 100644 --- a/lib/pages/first/pages/first_page.dart +++ b/lib/pages/first/pages/first_page.dart @@ -70,10 +70,12 @@ class _RootPageState extends State { AppNav.to(SearchPage()); }, ), - Expanded( + _homeDatas.isEmpty ? SizedBox() : Expanded( child: CustomScrollView( slivers: [ - SliverToBoxAdapter(child: HomeFirstView()), + SliverToBoxAdapter(child: HomeFirstView( + categories: _categories, + )), SliverPadding( padding: const EdgeInsets.only(left: 16, right: 10, top: 16, bottom: 8), sliver: SliverToBoxAdapter( @@ -83,7 +85,11 @@ class _RootPageState extends State { SliverPadding( padding: const EdgeInsetsGeometry.symmetric(horizontal: 16), sliver: SliverToBoxAdapter( - child: HomeRankWidget(), + child: HomeRankWidget( + dramaRanks: _homeDatas + .firstWhere((element) => element.moduleKey == 'week_ranking') + .dataList, + ), ), ), const SliverToBoxAdapter(child: HomeRemainingSections()), diff --git a/lib/pages/first/pages/search_page.dart b/lib/pages/first/pages/search_page.dart index 8546c41..19ff582 100644 --- a/lib/pages/first/pages/search_page.dart +++ b/lib/pages/first/pages/search_page.dart @@ -1,9 +1,18 @@ import 'package:flutter/material.dart'; +import 'package:novyronst/common/api/video_request.dart'; import 'package:novyronst/common/const/const_color.dart'; +import 'package:novyronst/common/const/const_string.dart'; +import 'package:novyronst/common/help/app_nav.dart'; +import 'package:novyronst/common/help/help_image.dart'; +import 'package:novyronst/common/help/help_time.dart'; +import 'package:novyronst/common/model/ny_drama_model.dart'; +import 'package:novyronst/pages/first/pages/search_result_page.dart'; import 'package:novyronst/pages/first/widgets/recent_history_view.dart'; import 'package:novyronst/pages/first/widgets/search_view.dart'; +import 'package:novyronst/pages/widgets/ny_tag_widget.dart'; import 'package:novyronst/tools/util/search_history_util.dart'; import 'package:novyronst/tools/widgets/novy_app_bar.dart'; +import 'package:novyronst/tools/widgets/novy_com_widget.dart'; class SearchPage extends StatefulWidget { const SearchPage({super.key}); @@ -18,6 +27,7 @@ class _SearchPageState extends State { List _history = []; final _searchController = TextEditingController(); + List _trendingSearches = []; Future _loadSearchHistory() async { final list = await SearchHistoryUtil.getSearchList(); @@ -27,13 +37,10 @@ class _SearchPageState extends State { }); } - Future _onGoSearch(String keyword,) async { + Future _onGoSearch(String keyword) async { await SearchHistoryUtil.addSearch(keyword); await _loadSearchHistory(); - // HelpNav.to(ResultPage( - // keyword: keyword, - // )); - // 发起 搜索 + AppNav.to(SearchResultPage(searchText: keyword)); } @@ -41,25 +48,40 @@ class _SearchPageState extends State { return _history.isEmpty ? SizedBox() : RecentHistoryView( searchHistory: _history, onTapClear: () { - // HelpNav.showDialog(ComConfirmWidget( - // title: 'Clear Search History?', - // )); + _history.clear(); setState(() {}); }, onTapHistory: (value) { print('object: $value'); - // HelpNav.to(ResultPage(keyword: value)); + AppNav.to(SearchResultPage(searchText: value)); }, ); } + void _getTrendingSearches() { + VideoRequest.getSearchTrending().then((value) { + setState(() { + _trendingSearches = value; + }); + }); + } + + @override + void initState() { + super.initState(); + + _getTrendingSearches(); + _loadSearchHistory(); + } + @override Widget build(BuildContext context) { return Scaffold( backgroundColor: ConstColor.withValue('#110D08'), appBar: NovyAppBar( titleWidget: SeSearchView( + controller: _searchController, onClickSearch: (keyword) => _onGoSearch(keyword), ), ), @@ -71,6 +93,90 @@ class _SearchPageState extends State { child: _buildHistoryView(), ) ), + const SliverToBoxAdapter(child: SizedBox(height: 16,)), + SliverList.separated( + itemBuilder: (context, index) { + return TrendingSearchItem(model: _trendingSearches[index]); + }, + itemCount: _trendingSearches.length, + separatorBuilder: (context, index) => const SizedBox(height: 12), + ) + ], + ), + ); + } +} + +class TrendingSearchItem extends StatelessWidget { + const TrendingSearchItem({super.key, + required this.model, + this.toCate = true, + }); + + final NyDramaModel model; + final bool toCate; + + @override + Widget build(BuildContext context) { + + Widget _tagWidget = SizedBox(); + String _tag = ''; + if (toCate) { + if (model.category!.isNotEmpty) { + _tag = '${model.category!.first}'; + } + _tagWidget = NyTagWidget( + text: _tag, + bgColor: Colors.white.withAlpha(127), + textColor: Colors.white.withAlpha(127), + ); + } else { + if (model.categoryList!.isNotEmpty) { + _tag = model.categoryList!.first.name; + } + _tagWidget = NyTagWidget( + text: _tag, + bgColor: Colors.white.withAlpha(127), + textColor: Colors.white.withAlpha(127), + ); + } + + + + return Container( + height: 96, + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Row( + children: [ + HelpImage.network(model.imageUrl, width: 72, height: 96), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + NovyText(model.name, fontSize: 14, + fontFamily: ConstFont.montBold, + ), + SizedBox( + height: 36, + child: NovyText(model.description, fontSize: 11, + color: Colors.white.withAlpha(127), + maxLines: 2, + ), + ), + const SizedBox(height: 5), + Row( + children: [ + _tagWidget, + if (_tag!='') const SizedBox(width: 12), + NyHotWidget(text: HelpTime.formatNum(model.watchTotal), + textColor: ConstColor.btGreen, + ) + ], + ) + ], + ), + ) ], ), ); diff --git a/lib/pages/first/pages/search_result_page.dart b/lib/pages/first/pages/search_result_page.dart index e69de29..4a896cd 100644 --- a/lib/pages/first/pages/search_result_page.dart +++ b/lib/pages/first/pages/search_result_page.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; +import 'package:novyronst/common/api/video_request.dart'; +import 'package:novyronst/common/model/ny_drama_model.dart'; +import 'package:novyronst/pages/first/pages/search_page.dart'; +import 'package:novyronst/pages/widgets/ny_empty_view.dart'; +import 'package:novyronst/tools/util/search_history_util.dart'; +import 'package:novyronst/tools/widgets/app_toast_util.dart'; + +import '../../../common/const/const_color.dart'; +import '../../../tools/widgets/novy_app_bar.dart'; +import '../widgets/search_view.dart'; + +class SearchResultPage extends StatefulWidget { + + const SearchResultPage({super.key, + required this.searchText, + }); + + final String searchText; + + @override + State createState() { + return _SearchResultPageState(); + } +} + +class _SearchResultPageState extends State { + + final _searchController = TextEditingController(); + final _searchFocus = FocusNode(); + List _searchResult = []; + bool _isLoading = false; + + @override + void initState() { + super.initState(); + + _searchController.text = widget.searchText; + _searchList(); + } + + void _searchList() async { + String keyword = _searchController.text; + if (keyword.trim().isEmpty) { + _searchFocus.requestFocus(); + return; + } + _isLoading = true; + AppToastUtil.showLoading(); + // await SearchHistoryUtil.addSearch(keyword); + VideoRequest.search(keyword: keyword).then((valuer) { + _isLoading = false; + AppToastUtil.dismiss(); + setState(() { + _searchResult = valuer; + }); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: ConstColor.withValue('#110D08'), + appBar: NovyAppBar( + titleWidget: SeSearchView( + controller: _searchController, + focusNode: _searchFocus, + onClickSearch: (keyword) => _searchList(), + ), + ), + body: _searchResult.isEmpty && !_isLoading ? NyEmptyView() : ListView.separated( + itemBuilder: (context, index) { + return TrendingSearchItem( + model: _searchResult[index], + toCate: false, + ); + }, + separatorBuilder: (context, index) => SizedBox(height: 12), + itemCount: _searchResult.length + ) + ); + } +} \ No newline at end of file diff --git a/lib/pages/first/widgets/home_first_view.dart b/lib/pages/first/widgets/home_first_view.dart index 5149a75..525178e 100644 --- a/lib/pages/first/widgets/home_first_view.dart +++ b/lib/pages/first/widgets/home_first_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:novyronst/common/model/ny_category_model.dart'; import 'package:novyronst/pages/widgets/cut_corner_clipper.dart'; import 'package:novyronst/pages/widgets/ny_progress_widget.dart'; import 'package:novyronst/pages/widgets/ny_tag_widget.dart'; @@ -10,7 +11,11 @@ import 'package:novyronst/tools/widgets/novy_img_bg.dart'; class HomeFirstView extends StatefulWidget { - const HomeFirstView({super.key}); + const HomeFirstView({super.key, + required this.categories, + }); + + final List categories; @override State createState() { @@ -46,14 +51,15 @@ class _HomeFirstViewState extends State { SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( - children: List.generate(3, (index) { + children: List.generate(widget.categories.length, (index) { + final _item = widget.categories[index]; return Container( margin: const EdgeInsets.only(right: 8), child: NovyImgBg(imgBg: ConstImage.cateBg, height: 52, child: Stack( children:[ - NovyText('ClassPp', color: ConstColor.btGreen, + NovyText(_item.name, color: ConstColor.btGreen, fontSize: 21, textAlign: TextAlign.center, fontFamily: ConstFont.montBlack, diff --git a/lib/pages/first/widgets/home_rank_widget.dart b/lib/pages/first/widgets/home_rank_widget.dart index b646277..5fe22b4 100644 --- a/lib/pages/first/widgets/home_rank_widget.dart +++ b/lib/pages/first/widgets/home_rank_widget.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:novyronst/common/const/const_color.dart'; import 'package:novyronst/common/const/const_string.dart'; +import 'package:novyronst/common/help/help_image.dart'; +import 'package:novyronst/common/model/ny_drama_model.dart'; import 'package:novyronst/tools/widgets/novy_com_widget.dart'; import '../../../tools/util/app_screen.dart'; @@ -10,7 +13,12 @@ import '../../widgets/ny_tag_widget.dart'; class HomeRankWidget extends StatelessWidget { - const HomeRankWidget({super.key}); + const HomeRankWidget({ + super.key, + required this.dramaRanks, + }); + + final List dramaRanks; @override Widget build(BuildContext context) { @@ -20,7 +28,7 @@ class HomeRankWidget extends StatelessWidget { cutSize: 40, child: Padding( padding: const EdgeInsets.all(8), - child: Column( + child: dramaRanks.isEmpty ? SizedBox(height: 20,) : Column( children: [ _buildRankList(), const SizedBox(height: 5), @@ -37,6 +45,7 @@ class HomeRankWidget extends StatelessWidget { } Row _buildRankList() { + return Row( children: [ _firstVideoView(), @@ -47,38 +56,116 @@ class HomeRankWidget extends StatelessWidget { } Widget _firstVideoView() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 160.w, height: 240.w, - decoration: BoxDecoration( - color: Colors.red, - ) - ), - const SizedBox(height: 10), - NovyText('Star of the ocean', fontSize: 15, - color: Colors.black, - fontFamily: ConstFont.montMedium, - ), - const SizedBox(height: 5), - NyTagWidget(text: 'Safiray'), - const SizedBox(height: 8), - ], + NyDramaModel _item = dramaRanks.first; + String _tag = "Safiray"; + if (_item.categoryList!=null && _item.categoryList!.isNotEmpty) { + _tag = _item.categoryList!.first.name; + } + return SizedBox( + width: 160.w, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + SizedBox( + width: 160.w, height: 240.w, + child: HelpImage.network(_item.imageUrl, fit: BoxFit.cover), + ), + Positioned( + top: -10, left: 1, + child: _colorsText(1), + ) + ], + ), + const SizedBox(height: 10), + NovyText(_item.name, fontSize: 15, + color: Colors.black, + fontFamily: ConstFont.montMedium, + ), + const SizedBox(height: 5), + Row( + children: [ + NyTagWidget(text: _tag), + ], + ), + const SizedBox(height: 8), + ], + ), ); } Widget _otherVideoView() { + int _count = dramaRanks.length - 1; + if (_count > 3) _count = 3; return Column( - children: List.generate(3, (index) { + children: List.generate(_count, (index) { + NyDramaModel _item = dramaRanks[index + 1]; + String _tag = ""; + if (_item.categoryList!=null && _item.categoryList!.isNotEmpty) { + _tag = _item.categoryList!.first.name; + } + return Container( margin: const EdgeInsets.only(bottom: 8), width: 160.w, height: 93.w, - decoration: BoxDecoration( - color: Colors.grey, - ) + child: Row( + children: [ + HelpImage.network(_item.imageUrl, width: 62.w, height: 93.w), + const SizedBox(width: 8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _colorsText(index + 2), + const SizedBox(height: 8), + NovyText(_item.name, fontSize: 13, + color: Colors.black, + ), + const SizedBox(height: 5), + _tag.isNotEmpty ? NyTagWidget(text: _tag) : SizedBox() + ], + ), + ) + ], + ), ); }) ); } + + _colorsText(int index) { + if (index == 4) { + return Text( + '$index', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: Colors.grey.withAlpha(127), + fontStyle: FontStyle.italic, + ), + ); + } + return ShaderMask( + shaderCallback: (Rect bounds) { + return LinearGradient( + colors: [ + ConstColor.withValue('#3ED71F'), + ConstColor.withValue('#FFFF65'), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ).createShader(bounds); + }, + child: Text( + '$index', + style: TextStyle( + fontSize: index==1 ? 40: 20, + fontWeight: FontWeight.bold, + color: Colors.white, + fontStyle: FontStyle.italic, + ), + ), + ); + } } \ No newline at end of file diff --git a/lib/pages/first/widgets/home_remaining_sections.dart b/lib/pages/first/widgets/home_remaining_sections.dart index 7c92dda..69fcfbc 100644 --- a/lib/pages/first/widgets/home_remaining_sections.dart +++ b/lib/pages/first/widgets/home_remaining_sections.dart @@ -8,28 +8,6 @@ import 'package:novyronst/tools/widgets/novy_com_widget.dart'; class HomeRemainingSections extends StatelessWidget { const HomeRemainingSections({super.key}); - static const List<_DramaItem> _rankingItems = [ - _DramaItem( - title: 'Star Of The Ocean', - category: 'Period Drama', - palette: [Color(0xFF6E4968), Color(0xFFE5A3B8)], - ), - _DramaItem( - title: 'Not Your Bride', - category: 'Period Drama', - palette: [Color(0xFF262431), Color(0xFFE0A15F)], - ), - _DramaItem( - title: 'Not Your Bride', - category: 'Period Drama', - palette: [Color(0xFF261B16), Color(0xFFD9C2A2)], - ), - _DramaItem( - title: 'Not Your Bride', - category: 'Period Drama', - palette: [Color(0xFF223E36), Color(0xFFFFC568)], - ), - ]; static const _ActorInfo _actor = _ActorInfo( name: 'Cheng Zi', @@ -84,8 +62,7 @@ class HomeRemainingSections extends StatelessWidget { padding: const EdgeInsets.fromLTRB(16, 10, 16, 92), child: Column( children: [ - const _RankingSection(items: _rankingItems), - const SizedBox(height: 18), + const SizedBox(height: 10), const _ActorSection(actor: _actor, items: _seriesItems), const SizedBox(height: 18), ..._categoryTitles.map((title) { @@ -100,51 +77,6 @@ class HomeRemainingSections extends StatelessWidget { } } -class _RankingSection extends StatelessWidget { - const _RankingSection({required this.items}); - - final List<_DramaItem> items; - - @override - Widget build(BuildContext context) { - return Container( - padding: const EdgeInsets.all(5), - decoration: BoxDecoration( - color: Colors.white, - border: Border.all(color: Colors.black, width: 2), - ), - child: Column( - children: [ - SizedBox( - height: 230, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded(flex: 6, child: _TopRankingPoster(item: items.first)), - const SizedBox(width: 8), - Expanded( - flex: 5, - child: Column( - children: List.generate(3, (index) { - return Expanded( - child: _RankingListItem( - rank: index + 2, - item: items[index + 1], - ), - ); - }), - ), - ), - ], - ), - ), - const SizedBox(height: 5), - const _GreenActionButton(text: 'View more rankings'), - ], - ), - ); - } -} class _TopRankingPoster extends StatelessWidget { const _TopRankingPoster({required this.item}); diff --git a/lib/pages/first/widgets/search_view.dart b/lib/pages/first/widgets/search_view.dart index d0d5e03..1b3cdac 100644 --- a/lib/pages/first/widgets/search_view.dart +++ b/lib/pages/first/widgets/search_view.dart @@ -94,9 +94,13 @@ class _SearchViewState extends State { class SeSearchView extends StatefulWidget { const SeSearchView({super.key, this.onClickSearch, + required this.controller, + this.focusNode, }); final Function(String)? onClickSearch; + final TextEditingController controller; + final FocusNode? focusNode; @override State createState() { @@ -106,7 +110,12 @@ class SeSearchView extends StatefulWidget { class _SeSearchViewState extends State { - final _searchController = TextEditingController(); + + @override + void initState() { + super.initState(); + + } @override Widget build(BuildContext context) { @@ -123,8 +132,8 @@ class _SeSearchViewState extends State { Icon(Icons.search, color: Colors.white, size: 24), Expanded(child: CupertinoTextField( - controller: _searchController, - // focusNode: widget.focusNode, + controller: widget.controller, + focusNode: widget.focusNode, decoration: BoxDecoration(), placeholder: 'What are...... ', placeholderStyle: TextStyle( @@ -137,8 +146,8 @@ class _SeSearchViewState extends State { ), textInputAction: TextInputAction.search, onEditingComplete: () { - if (_searchController.text.trim().isNotEmpty) { - widget.onClickSearch?.call(_searchController.text); + if (widget.controller.text.trim().isNotEmpty) { + widget.onClickSearch?.call(widget.controller.text); } }, ),), @@ -146,8 +155,8 @@ class _SeSearchViewState extends State { HelpImage.asset(ConstImage.homeSearch, width: 78, height: 36, fit: BoxFit.fill ).addInkWell(onTap: () { - if (_searchController.text.trim().isNotEmpty) { - widget.onClickSearch?.call(_searchController.text); + if (widget.controller.text.trim().isNotEmpty) { + widget.onClickSearch?.call(widget.controller.text); } }) diff --git a/lib/pages/play/pages/play_page.dart b/lib/pages/play/pages/play_page.dart index 6c0f24c..d899b80 100644 --- a/lib/pages/play/pages/play_page.dart +++ b/lib/pages/play/pages/play_page.dart @@ -1,7 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:novyronst/common/api/video_request.dart'; +import 'package:novyronst/common/model/ny_drama_model.dart'; +import 'package:novyronst/pages/play/widgets/player_view.dart'; class PlayPage extends StatefulWidget { - const PlayPage({super.key}); + const PlayPage({super.key, this.isActive = true}); + + final bool isActive; @override State createState() { @@ -10,9 +15,114 @@ class PlayPage extends StatefulWidget { } class _RootPageState extends State { + final List _videoList = []; + int _page = 1; + bool _isLoading = false; + bool _isLoadingMore = false; + bool _hasMore = true; + + @override + void initState() { + super.initState(); + _getVideoList(refresh: true); + } + + Future _getVideoList({bool refresh = false}) async { + if (_isLoading || _isLoadingMore) return; + if (!refresh && !_hasMore) return; + + final nextPage = refresh ? 1 : _page + 1; + setState(() { + if (refresh) { + _isLoading = true; + } else { + _isLoadingMore = true; + } + }); + + final value = await VideoRequest.getRecommandsList(page: nextPage); + if (!mounted) return; + + setState(() { + if (refresh) { + _videoList + ..clear() + ..addAll(value); + } else { + _videoList.addAll(value); + } + + _page = nextPage; + _hasMore = value.isNotEmpty; + _isLoading = false; + _isLoadingMore = false; + }); + } + + void _handlePageChanged(int index) { + if (index >= _videoList.length - 1) { + _getVideoList(); + } + } + + Future _toggleCollect(int index) async { + if (index < 0 || index >= _videoList.length) return; + + final item = _videoList[index]; + final videoId = item.shortPlayVideoId ?? item.id; + if (videoId == null) return; + + final shortPlayId = item.shortPlayId ?? item.shortId; + final oldIsCollect = item.isCollect ?? false; + final oldCollectTotal = item.collectTotal; + final newIsCollect = !oldIsCollect; + final newCollectTotal = oldCollectTotal + (newIsCollect ? 1 : -1); + + setState(() { + _videoList[index] = item.copyWith( + isCollect: newIsCollect, + collectTotal: newCollectTotal < 0 ? 0 : newCollectTotal, + ); + }); + + final success = newIsCollect + ? await VideoRequest.collect(shortPlayId: shortPlayId, videoId: videoId) + : await VideoRequest.cancelCollect(shortPlayId: shortPlayId); + + if (!mounted || success) return; + + setState(() { + _videoList[index] = _videoList[index].copyWith( + isCollect: oldIsCollect, + collectTotal: oldCollectTotal, + ); + }); + } @override Widget build(BuildContext context) { - return Scaffold(); + return Scaffold(backgroundColor: Colors.black, body: _buildBody()); } -} \ No newline at end of file + + Widget _buildBody() { + if (_isLoading && _videoList.isEmpty) { + return const Center( + child: CircularProgressIndicator(color: Colors.white), + ); + } + + if (_videoList.isEmpty) { + return const Center( + child: Text('No videos', style: TextStyle(color: Colors.white70)), + ); + } + + return PlayerView( + videos: _videoList, + isActive: widget.isActive, + isLoadingMore: _isLoadingMore, + onPageChanged: _handlePageChanged, + onCollectTap: _toggleCollect, + ); + } +} diff --git a/lib/pages/play/widgets/player_view.dart b/lib/pages/play/widgets/player_view.dart new file mode 100644 index 0000000..c6a43f7 --- /dev/null +++ b/lib/pages/play/widgets/player_view.dart @@ -0,0 +1,477 @@ +import 'package:flutter/material.dart'; +import 'package:novyronst/common/const/const_color.dart'; +import 'package:novyronst/common/model/ny_drama_model.dart'; +import 'package:novyronst/tools/widgets/novy_com_widget.dart'; +import 'package:video_player/video_player.dart'; + +class PlayerView extends StatefulWidget { + const PlayerView({ + super.key, + required this.videos, + this.initialIndex = 0, + this.onPageChanged, + this.onCollectTap, + this.isLoadingMore = false, + this.isActive = true, + }); + + final List videos; + final int initialIndex; + final ValueChanged? onPageChanged; + final ValueChanged? onCollectTap; + final bool isLoadingMore; + final bool isActive; + + @override + State createState() => _PlayerViewState(); +} + +class _PlayerViewState extends State { + late final PageController _pageController; + final Map _controllers = {}; + final Map> _initializingControllers = {}; + int _currentIndex = 0; + + @override + void initState() { + super.initState(); + _currentIndex = widget.videos.isEmpty + ? 0 + : widget.initialIndex.clamp(0, widget.videos.length - 1); + _pageController = PageController(initialPage: _currentIndex); + _cachePlaybackWindow(_currentIndex); + widget.isActive ? _playActiveVideo() : _pauseAllVideos(); + } + + @override + void didUpdateWidget(covariant PlayerView oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.videos.isEmpty) return; + + if (_currentIndex >= widget.videos.length) { + _currentIndex = widget.videos.length - 1; + } + + _cachePlaybackWindow(_currentIndex); + widget.isActive ? _playActiveVideo() : _pauseAllVideos(); + } + + @override + void dispose() { + _pageController.dispose(); + for (final controller in _controllers.values) { + controller + ..removeListener(_handleControllerChanged) + ..dispose(); + } + _controllers.clear(); + _initializingControllers.clear(); + super.dispose(); + } + + Future _cachePlaybackWindow(int index) async { + final keepIndexes = {index}; + if (index + 1 < widget.videos.length) { + keepIndexes.add(index + 1); + } + + final disposeIndexes = _controllers.keys + .where((controllerIndex) => !keepIndexes.contains(controllerIndex)) + .toList(); + for (final controllerIndex in disposeIndexes) { + final controller = _controllers.remove(controllerIndex); + _initializingControllers.remove(controllerIndex); + controller + ?..removeListener(_handleControllerChanged) + ..dispose(); + } + + await _ensureController(index); + if (index + 1 < widget.videos.length) { + await _ensureController(index + 1); + } + } + + Future _ensureController(int index) async { + if (index < 0 || index >= widget.videos.length) return null; + final cachedController = _controllers[index]; + if (cachedController != null) { + if (cachedController.value.isInitialized) return cachedController; + final initializingController = _initializingControllers[index]; + if (initializingController != null) return initializingController; + return cachedController; + } + + final videoUrl = resolveDramaVideoUrl(widget.videos[index]); + if (videoUrl == null) return null; + + final controller = VideoPlayerController.networkUrl(Uri.parse(videoUrl)); + _controllers[index] = controller; + controller.addListener(_handleControllerChanged); + + final initializingController = _initializeController(index, controller); + _initializingControllers[index] = initializingController; + return initializingController; + } + + Future _initializeController( + int index, + VideoPlayerController controller, + ) async { + try { + await controller.initialize(); + await controller.setLooping(true); + if (!mounted || _controllers[index] != controller) return controller; + _initializingControllers.remove(index); + setState(() {}); + if (index == _currentIndex && + shouldPlayActiveVideo( + controller.value, + isPageActive: widget.isActive, + )) { + await controller.play(); + } + } catch (_) { + if (_controllers[index] == controller) { + _controllers.remove(index); + } + _initializingControllers.remove(index); + controller + ..removeListener(_handleControllerChanged) + ..dispose(); + if (mounted) setState(() {}); + return null; + } + + return controller; + } + + Future _playActiveVideo() async { + if (!widget.isActive) { + await _pauseAllVideos(); + return; + } + + for (final entry in _controllers.entries) { + if (entry.key != _currentIndex && entry.value.value.isPlaying) { + await entry.value.pause(); + } + } + + final controller = await _ensureController(_currentIndex); + if (!mounted || + controller == null || + _currentIndex >= widget.videos.length) { + return; + } + if (shouldPlayActiveVideo( + controller.value, + isPageActive: widget.isActive, + )) { + await controller.play(); + } + } + + Future _pauseAllVideos() async { + for (final controller in _controllers.values) { + if (controller.value.isPlaying) { + await controller.pause(); + } + } + } + + void _handleControllerChanged() { + if (mounted) { + setState(() {}); + } + } + + void _handlePageChanged(int index) { + _currentIndex = index; + widget.onPageChanged?.call(index); + _cachePlaybackWindow(index); + widget.isActive ? _playActiveVideo() : _pauseAllVideos(); + } + + void _togglePlayback() { + final controller = _controllers[_currentIndex]; + if (!widget.isActive || + controller == null || + !controller.value.isInitialized) { + return; + } + controller.value.isPlaying ? controller.pause() : controller.play(); + } + + @override + Widget build(BuildContext context) { + if (widget.videos.isEmpty) { + return const SizedBox.shrink(); + } + + return PageView.builder( + controller: _pageController, + scrollDirection: Axis.vertical, + itemCount: widget.videos.length, + onPageChanged: _handlePageChanged, + itemBuilder: (context, index) { + return _PlayerPage( + drama: widget.videos[index], + controller: _controllers[index], + isPageActive: widget.isActive, + isLoadingMore: + widget.isLoadingMore && index == widget.videos.length - 1, + onTap: _togglePlayback, + onCollectTap: () => widget.onCollectTap?.call(index), + ); + }, + ); + } +} + +class _PlayerPage extends StatelessWidget { + const _PlayerPage({ + required this.drama, + required this.controller, + required this.isPageActive, + required this.isLoadingMore, + required this.onTap, + required this.onCollectTap, + }); + + final NyDramaModel drama; + final VideoPlayerController? controller; + final bool isPageActive; + final bool isLoadingMore; + final VoidCallback onTap; + final VoidCallback onCollectTap; + + @override + Widget build(BuildContext context) { + final bottomPadding = MediaQuery.paddingOf(context).bottom + 86; + + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onTap, + child: Stack( + fit: StackFit.expand, + children: [ + ColoredBox(color: Colors.black, child: _buildVideoBody()), + const _BottomShade(), + if (isPageActive && shouldShowPauseIcon(controller?.value)) + const _PauseIndicator(), + Positioned( + left: 16, + right: 16, + bottom: bottomPadding - 10, + child: _buildMeta(), + ), + if (isLoadingMore) + Positioned( + left: 0, + right: 0, + bottom: bottomPadding - 30, + child: const Center( + child: SizedBox( + width: 18, + height: 18, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Colors.white, + ), + ), + ), + ), + ], + ), + ); + } + + Widget _buildVideoBody() { + final activeController = controller; + if (activeController != null && activeController.value.isInitialized) { + final size = activeController.value.size; + return FittedBox( + fit: BoxFit.cover, + child: SizedBox( + width: size.width, + height: size.height, + child: VideoPlayer(activeController), + ), + ); + } + + return Stack( + fit: StackFit.expand, + children: [ + if (drama.imageUrl.isNotEmpty) + Image.network( + drama.imageUrl, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) => + const SizedBox.shrink(), + ), + Center( + child: resolveDramaVideoUrl(drama) == null + ? const Icon( + Icons.videocam_off_outlined, + color: Colors.white54, + size: 42, + ) + : const CircularProgressIndicator(color: Colors.white), + ), + ], + ); + } + + Widget _buildMeta() { + final isCollect = drama.isCollect ?? false; + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + NovyText( + drama.name, + fontSize: 20, + fontWeight: FontWeight.w700, + maxLines: 1, + shadow: const Shadow(color: Colors.black54, blurRadius: 8), + ), + const SizedBox(height: 8), + NovyText( + drama.description, + color: Colors.white.withValues(alpha: 0.82), + fontSize: 14, + maxLines: 2, + lineHeight: 1.3, + shadow: const Shadow(color: Colors.black54, blurRadius: 8), + ), + ], + ), + ), + const SizedBox(width: 16), + SizedBox( + width: 56, + child: IconButton( + onPressed: onCollectTap, + iconSize: 34, + color: isCollect ? ConstColor.btGreen : Colors.white, + icon: Icon(isCollect ? Icons.favorite : Icons.favorite_border), + ), + ), + ], + ), + const SizedBox(height: 16), + _buildProgress(), + ], + ); + } + + Widget _buildProgress() { + final activeController = controller; + if (activeController != null && activeController.value.isInitialized) { + return VideoProgressIndicator( + activeController, + allowScrubbing: true, + padding: EdgeInsets.zero, + colors: VideoProgressColors( + playedColor: ConstColor.btGreen, + bufferedColor: Colors.white.withValues(alpha: 0.36), + backgroundColor: Colors.white.withValues(alpha: 0.18), + ), + ); + } + + return ClipRRect( + borderRadius: BorderRadius.circular(2), + child: LinearProgressIndicator( + minHeight: 3, + value: 0, + color: ConstColor.btGreen, + backgroundColor: Colors.white.withValues(alpha: 0.18), + ), + ); + } +} + +class _BottomShade extends StatelessWidget { + const _BottomShade(); + + @override + Widget build(BuildContext context) { + return IgnorePointer( + child: DecoratedBox( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withValues(alpha: 0.12), + Colors.black.withValues(alpha: 0.72), + ], + stops: const [0.42, 0.68, 1], + ), + ), + ), + ); + } +} + +class _PauseIndicator extends StatelessWidget { + const _PauseIndicator(); + + @override + Widget build(BuildContext context) { + return IgnorePointer( + child: Center( + child: Container( + width: 72, + height: 72, + decoration: BoxDecoration( + color: Colors.black.withValues(alpha: 0.38), + shape: BoxShape.circle, + border: Border.all(color: Colors.white.withValues(alpha: 0.28)), + ), + child: const Icon( + Icons.play_arrow_outlined, + color: Colors.white, + size: 42, + ), + ), + ), + ); + } +} + +String? resolveDramaVideoUrl(NyDramaModel drama) { + final infoUrl = drama.videoInfo?['video_url']?.toString().trim(); + if (infoUrl != null && infoUrl.isNotEmpty) return infoUrl; + + final videoUrl = drama.videoUrl?.trim(); + if (videoUrl != null && videoUrl.isNotEmpty) return videoUrl; + + return null; +} + +bool shouldShowPauseIcon(VideoPlayerValue? value) { + return value != null && value.isInitialized && !value.isPlaying; +} + +bool shouldPlayActiveVideo( + VideoPlayerValue? value, { + required bool isPageActive, +}) { + return isPageActive && + value != null && + value.isInitialized && + !value.isPlaying; +} diff --git a/lib/pages/widgets/ny_empty_view.dart b/lib/pages/widgets/ny_empty_view.dart new file mode 100644 index 0000000..39f3384 --- /dev/null +++ b/lib/pages/widgets/ny_empty_view.dart @@ -0,0 +1,81 @@ +import 'package:flutter/material.dart'; +import 'package:novyronst/common/const/const_color.dart'; +import 'package:novyronst/common/help/help_image.dart'; +import 'package:novyronst/tools/widgets/novy_com_widget.dart'; + +class NyEmptyView extends StatelessWidget { + + const NyEmptyView({super.key, + this.title = 'No Data', + // 'Nothing here yet.\nCome back later for more fun!', + // Something went wrong.\nPlease try again. + this.subtitle = 'Nothing here yet.\nCome back later for more fun!', + this.btnText = 'RETRY', + this.imgName, + }); + + final String title; + final String subtitle; + final String btnText; + final String? imgName; + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 100), + HelpImage.asset(ConstImage.emptyImg, width: 136), + const SizedBox(height: 14), + // _customText(title), + // const SizedBox(height: 10), + NovyText( title, + fontSize: 16, + color: Colors.white.withAlpha(127), + textAlign: TextAlign.center, + ), + // const SizedBox(height: 14), + + ], + ), + ); + } + + _customText(String text) { + return Text( + text, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 16, + color: Colors.white, // 内部字体白色 + fontStyle: FontStyle.italic, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + shadows: [ + Shadow( + color: Colors.black, // 描边颜色 + offset: Offset(1, 1), // 偏移量 + blurRadius: 0, // 不模糊,形成硬边描边 + ), + Shadow( + color: Colors.black, + offset: Offset(-1, -1), + blurRadius: 0, + ), + Shadow( + color: Colors.black, + offset: Offset(1, -1), + blurRadius: 0, + ), + Shadow( + color: Colors.black, + offset: Offset(-1, 1), + blurRadius: 0, + ), + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/pages/widgets/ny_tag_widget.dart b/lib/pages/widgets/ny_tag_widget.dart index 1b2ee6d..3cab449 100644 --- a/lib/pages/widgets/ny_tag_widget.dart +++ b/lib/pages/widgets/ny_tag_widget.dart @@ -36,9 +36,11 @@ class NyTagWidget extends StatelessWidget { class NyHotWidget extends StatelessWidget { const NyHotWidget({super.key, required this.text, + this.textColor, }); final String text; + final Color? textColor; @override Widget build(BuildContext context) { @@ -47,7 +49,7 @@ class NyHotWidget extends StatelessWidget { HelpImage.asset(ConstImage.hot, width: 12), NovyText(text, fontSize: 10, - color: Colors.black, + color: textColor ?? Colors.black, ) ], ); diff --git a/lib/root/root_page.dart b/lib/root/root_page.dart index 5a1f554..2142ab7 100644 --- a/lib/root/root_page.dart +++ b/lib/root/root_page.dart @@ -21,47 +21,51 @@ class RootPage extends StatefulWidget { } class _RootPageState extends State { - final _pageController = PageController(); - final List _pages = [ - NovyKeepAlive(child: FirstPage()), - NovyKeepAlive(child: PlayPage()), - CollectionPage(), - NovyKeepAlive(child: MyPage()), - ]; + int _currentIndex = 0; @override Widget build(BuildContext context) { + final pages = [ + NovyKeepAlive(child: FirstPage()), + NovyKeepAlive(child: PlayPage(isActive: _currentIndex == 1)), + CollectionPage(), + NovyKeepAlive(child: MyPage()), + ]; + return Scaffold( body: Stack( children: [ PageView( physics: const NeverScrollableScrollPhysics(), controller: _pageController, - children: _pages, + children: pages, ), Positioned( - left: 0, right: 0, bottom: 0, + left: 0, + right: 0, + bottom: 0, child: SafeArea( top: false, child: AppBottomView( onSelectedIndex: (index) { + setState(() { + _currentIndex = index; + }); _pageController.jumpToPage(index); }, ), ), - ) + ), ], - ) + ), ); } } /// 底部tabbar class AppBottomView extends StatefulWidget { - const AppBottomView({super.key, - required this.onSelectedIndex, - }); + const AppBottomView({super.key, required this.onSelectedIndex}); final Function(int) onSelectedIndex; @@ -72,14 +76,17 @@ class AppBottomView extends StatefulWidget { } class _AppBottomViewState extends State { - final List _unselectedIcons = [ - ConstImage.btHomeUnsel, ConstImage.btPlayUnsel, - ConstImage.btCollectUnsel, ConstImage.btMineUnsel + ConstImage.btHomeUnsel, + ConstImage.btPlayUnsel, + ConstImage.btCollectUnsel, + ConstImage.btMineUnsel, ]; final List _selectedIcons = [ - ConstImage.btHomeSel, ConstImage.btPlaySel, - ConstImage.btCollectSel, ConstImage.btMineSel + ConstImage.btHomeSel, + ConstImage.btPlaySel, + ConstImage.btCollectSel, + ConstImage.btMineSel, ]; final List _titles = ['Home', 'Play', 'Collect', 'Me']; int _currentIndex = 0; @@ -92,38 +99,45 @@ class _AppBottomViewState extends State { padding: const EdgeInsets.symmetric(horizontal: 16), child: Stack( children: [ - Opacity(opacity: 0.9, - child: ImageFiltered(imageFilter: ImageFilter.blur(sigmaX: 1), - child: HelpImage.asset(ConstImage.appBottom, - width: AppScreen.screenWidth-32, height: _height, + Opacity( + opacity: 0.9, + child: ImageFiltered( + imageFilter: ImageFilter.blur(sigmaX: 1), + child: HelpImage.asset( + ConstImage.appBottom, + width: AppScreen.screenWidth - 32, + height: _height, fit: BoxFit.fill, ), ), ), Positioned.fill( - child: Padding(padding: const EdgeInsets.symmetric(horizontal: 10), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: List.generate(_unselectedIcons.length, (index) { - return _buildTabItem(_titles[index], index, onTap: () { - setState(() { - _currentIndex = index; - }); - widget.onSelectedIndex(index); - - }); + return _buildTabItem( + _titles[index], + index, + onTap: () { + setState(() { + _currentIndex = index; + }); + widget.onSelectedIndex(index); + }, + ); }), ), ), - ) + ), ], ), ); } - _aaaa(){ - + _aaaa() { return ClipRRect( borderRadius: BorderRadius.circular(12), child: BackdropFilter( @@ -131,10 +145,7 @@ class _AppBottomViewState extends State { child: Container( color: Colors.white.withOpacity(0.1), child: const Center( - child: Text( - '毛玻璃效果', - style: TextStyle(color: Colors.white), - ), + child: Text('毛玻璃效果', style: TextStyle(color: Colors.white)), ), ), ), @@ -142,20 +153,23 @@ class _AppBottomViewState extends State { } Widget _buildTabItem(String title, int index, {required Function() onTap}) { - String icon = _currentIndex == index ? _selectedIcons[index] : _unselectedIcons[index]; - Color tcolor = _currentIndex == index ? ConstColor.btGreen : ConstColor.btWhite; + String icon = _currentIndex == index + ? _selectedIcons[index] + : _unselectedIcons[index]; + Color tcolor = _currentIndex == index + ? ConstColor.btGreen + : ConstColor.btWhite; return Container( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), - width: 60, height: 70, + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), + width: 60, + height: 70, child: Column( children: [ HelpImage.asset(icon, width: 32, height: 32), const SizedBox(height: 2), - NovyText(title, fontSize: 11, - color: tcolor, - ) + NovyText(title, fontSize: 11, color: tcolor), ], - ) + ), ).addInkWell(onTap: onTap); } -} \ No newline at end of file +}