From cbb9f60f7fef2b33db4414ef5155fe9d417e668d Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Wed, 21 Oct 2026 11:02:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(player):=20=E4=BA=92=E5=8A=A8=E6=A0=8F?= =?UTF-8?q?=E6=94=B9=E7=94=A8=E5=8F=82=E8=80=83=E5=9B=BE=E5=90=8C=E6=AC=BE?= =?UTF-8?q?=E5=9B=BE=E6=A0=87=E8=B5=84=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/icon/rail/rail_comment.png | Bin 0 -> 1316 bytes assets/icon/rail/rail_like.png | Bin 0 -> 928 bytes assets/icon/rail/rail_save.png | Bin 0 -> 1093 bytes assets/icon/rail/rail_share.png | Bin 0 -> 1004 bytes .../player/widgets/home_feed_overlay.dart | 13 ++--- .../player/widgets/interaction_rail.dart | 28 +++++------ lib/pages/player/widgets/player_overlay.dart | 13 ++--- pubspec.yaml | 1 + test/home/home_preview_layout_test.dart | 39 +++++++-------- test/player/player_fullscreen_test.dart | 45 +++++++++--------- 10 files changed, 61 insertions(+), 78 deletions(-) create mode 100644 assets/icon/rail/rail_comment.png create mode 100644 assets/icon/rail/rail_like.png create mode 100644 assets/icon/rail/rail_save.png create mode 100644 assets/icon/rail/rail_share.png diff --git a/assets/icon/rail/rail_comment.png b/assets/icon/rail/rail_comment.png new file mode 100644 index 0000000000000000000000000000000000000000..cc358c12db8aacc32579bf0f144ea12fe651397a GIT binary patch literal 1316 zcmV+<1>5?GP) zF>uo`6o!BI7!tOywQT9uu(cbom94PRv23ux2wPxlx3*imHEiuzHnIU*yAif#BW(3X zyNT2Be5bgvEy12~3bM2>WtYXH8J(4v4`Y!mRdKLHq{#VKV7fYED` z5*r4^<~#xLmCkhm;3a?#S`guD0M02zxWYDov!+G(TOpGBaEnvAw)ahla4z%?fOYsm z4B$C{A3h4sbLJ-e?YScLOjeSl_j z31Gv82tN|U<|SX#n9-<~cl@mG2>+WJUbls)jtFNh6`S)@w{F*s@aqLb@wPG1sbF*j zcVf27*YvWa5!r#e{g@TOhe8_Ry>{R~U8>ZKrW7u8i0*v&9LyI}f``1FO)T8`k%^ST zOQ1kDVzsu5l7gQ)IDhRjyf)t|lTv;gUg~NCd;xn^C&Kxy62w-gPZgbVgolo@91A

r0HJA7ML>bHP(Dlk6{OADZX+meba*Vp~5MW1Wn) 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 LNkvXXu0mjf_MsZ+ literal 0 HcmV?d00001 diff --git a/assets/icon/rail/rail_share.png b/assets/icon/rail/rail_share.png new file mode 100644 index 0000000000000000000000000000000000000000..59c79214fc6b2067e815b4c72815ec9cad3e54fc GIT binary patch literal 1004 zcmV zF>@0!5QX1)Twpq7Wc9&_T!0;f@wMpiGD9a7XBH7t-PK(kUZG zH=`(bw(g|U?MnJ)GRZiObN8}-TCG+}A%s9C9HW50E0YBe54Q-v1?G&w15xu>0ftbI z@OuqnLp}Zh%qWOwDZ=78&jGv#6DSM*2*6j%=av9&g8`HU7naZa3t*p%uT%GM8!EA@ zKhhBh6NBJ7YXD0CFF_Q_mhun4)Zyn>0A?TtWx<7~*CFNWu~|x8_ChM6Vi$a(K1 zfB>-zZl;f4Ffb{2IH^feN)cXaHuGz!i%2oT%}LV*0+LdMH~IQA1SItc*Jr4A5r8as z;7L;ImJS&G zxX+#C09V1~U-dyE;(D1RxmrpVyB<;Vu6K^`bEAAiHp; ztU9#sfXW4-ZPEtsn!ZMjX;QfRexkr=amNx zWIVM{j4Tcg$iCgo=eGSSyVAncvy-*4Y^bNjt=2Ltvf#Vf)O6E!?!ww;DYXUk1eqEU z?$q+OEy7c`6O)GkPT@#S3j=RO8Esjs%)s43Yz5E$jsN!HeFsF7;Ih({8l_QIm7eXL zra?310rN3{Z!zy;JJa&rh1#I%bze;jiax~-m8ERd5vCJ$9+fO#{stXZD%TFO6M5Xw z-|dtAE>qxu)UM2gv=mveav5;u>ISNhOM=dg#irCx>F<>2>Cs?F}lnjo?#l z5ai5p4`YlsOL?KyQn(9E?jD9dO=24&HnFKp?R~F7k`+5f!4K3_out??2);Bjs#A(b zSjXz&a{xa;9Lj5&g{A0h{^14(OIcZbpN-fY1fOlG*DR>}H8&4$YAz-3(=iFo9_p5Y a*th~1T { items: [ InteractionRailItem( key: const Key('rail-save'), - icon: Icons.star_rounded, - activeIcon: Icons.star_rounded, + asset: 'assets/icon/rail/rail_save.png', label: widget.l10n.favorite, count: widget.favoriteCount, countKey: const Key('rail-save-count'), @@ -183,8 +182,7 @@ class _HomeFeedOverlayState extends State { ), InteractionRailItem( key: const Key('rail-comments'), - icon: Icons.chat_bubble_outline_rounded, - activeIcon: Icons.chat_bubble_rounded, + asset: 'assets/icon/rail/rail_comment.png', label: widget.l10n.comments, count: widget.commentCount, countKey: const Key('rail-comments-count'), @@ -192,8 +190,7 @@ class _HomeFeedOverlayState extends State { ), InteractionRailItem( key: const Key('rail-like'), - icon: Icons.favorite_rounded, - activeIcon: Icons.favorite_rounded, + asset: 'assets/icon/rail/rail_like.png', label: widget.l10n.like, count: widget.likeCount, countKey: const Key('rail-like-count'), @@ -202,10 +199,8 @@ class _HomeFeedOverlayState extends State { ), InteractionRailItem( key: const Key('rail-share'), - icon: Icons.reply_rounded, - activeIcon: Icons.reply_rounded, + asset: 'assets/icon/rail/rail_share.png', label: widget.l10n.share, - mirrorIcon: true, onTap: widget.onShare, ), ], diff --git a/lib/pages/player/widgets/interaction_rail.dart b/lib/pages/player/widgets/interaction_rail.dart index dcd19e5..f3ff832 100644 --- a/lib/pages/player/widgets/interaction_rail.dart +++ b/lib/pages/player/widgets/interaction_rail.dart @@ -6,32 +6,28 @@ import '../../../core/formatting/count_format.dart'; import '../../../core/theme/app_colors.dart'; const double _buttonSize = 44; -const double _iconSize = 30; -const double _itemGap = 22; +const double _iconSize = 32; +const double _itemGap = 24; /// 互动栏单项:图标、计数与点击。 class InteractionRailItem { const InteractionRailItem({ required this.key, - required this.icon, - required this.activeIcon, + required this.asset, required this.label, required this.onTap, this.count, this.countKey, this.selected = false, - this.mirrorIcon = false, }); final Key key; - final IconData icon; - final IconData activeIcon; + final String asset; final String label; final VoidCallback onTap; final int? count; final Key? countKey; final bool selected; - final bool mirrorIcon; } /// 播放页与首页 feed 共用的右侧互动栏:收藏/评论/喜欢/分享。 @@ -61,10 +57,6 @@ class _RailButton extends StatelessWidget { @override Widget build(BuildContext context) { - final icon = Icon( - item.selected ? item.activeIcon : item.icon, - size: _iconSize, - ); return Column( key: item.key, mainAxisSize: MainAxisSize.min, @@ -74,10 +66,14 @@ class _RailButton extends StatelessWidget { child: IconButton( onPressed: item.onTap, padding: EdgeInsets.zero, - color: item.selected ? AppColors.blue : Colors.white, - icon: item.mirrorIcon - ? Transform.flip(flipX: true, child: icon) - : icon, + icon: Image.asset( + item.asset, + width: _iconSize, + height: _iconSize, + fit: BoxFit.contain, + color: item.selected ? AppColors.blue : Colors.white, + colorBlendMode: BlendMode.srcIn, + ), ), ), const SizedBox(height: 2), diff --git a/lib/pages/player/widgets/player_overlay.dart b/lib/pages/player/widgets/player_overlay.dart index f89e7ea..9896595 100644 --- a/lib/pages/player/widgets/player_overlay.dart +++ b/lib/pages/player/widgets/player_overlay.dart @@ -147,8 +147,7 @@ class PlayerPlaybackOverlay extends StatelessWidget { items: [ InteractionRailItem( key: const Key('player-rail-save'), - icon: Icons.star_rounded, - activeIcon: Icons.star_rounded, + asset: 'assets/icon/rail/rail_save.png', label: data.l10n.favorite, count: data.favoriteCount, countKey: const Key('player-rail-save-count'), @@ -157,8 +156,7 @@ class PlayerPlaybackOverlay extends StatelessWidget { ), InteractionRailItem( key: const Key('player-rail-comments'), - icon: Icons.chat_bubble_outline_rounded, - activeIcon: Icons.chat_bubble_rounded, + asset: 'assets/icon/rail/rail_comment.png', label: data.l10n.comments, count: data.commentCount, countKey: const Key('player-rail-comments-count'), @@ -166,8 +164,7 @@ class PlayerPlaybackOverlay extends StatelessWidget { ), InteractionRailItem( key: const Key('player-rail-like'), - icon: Icons.favorite_rounded, - activeIcon: Icons.favorite_rounded, + asset: 'assets/icon/rail/rail_like.png', label: data.l10n.like, count: data.likeCount, countKey: const Key('player-rail-like-count'), @@ -176,10 +173,8 @@ class PlayerPlaybackOverlay extends StatelessWidget { ), InteractionRailItem( key: const Key('player-rail-share'), - icon: Icons.reply_rounded, - activeIcon: Icons.reply_rounded, + asset: 'assets/icon/rail/rail_share.png', label: data.l10n.share, - mirrorIcon: true, onTap: actions.onShare, ), ], diff --git a/pubspec.yaml b/pubspec.yaml index 09d3088..5951cc5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -83,6 +83,7 @@ flutter: assets: - assets/icon/splash_logo.png - assets/icon/splash_logo_android12.png + - assets/icon/rail/ # To add assets to your application, add an assets section, like this: # assets: diff --git a/test/home/home_preview_layout_test.dart b/test/home/home_preview_layout_test.dart index f01ab09..876b140 100644 --- a/test/home/home_preview_layout_test.dart +++ b/test/home/home_preview_layout_test.dart @@ -150,38 +150,33 @@ void main() { tester.widget(find.byKey(const Key('rail-comments-count'))).data, '12', ); + String railAsset(Key key) { + final image = tester.widget( + find.descendant(of: find.byKey(key), matching: find.byType(Image)), + ); + return (image.image as AssetImage).assetName; + } + + expect(railAsset(const Key('rail-save')), 'assets/icon/rail/rail_save.png'); expect( - find.descendant( - of: find.byKey(const Key('rail-save')), - matching: find.byIcon(Icons.star_rounded), - ), - findsOneWidget, + railAsset(const Key('rail-comments')), + 'assets/icon/rail/rail_comment.png', ); + expect(railAsset(const Key('rail-like')), 'assets/icon/rail/rail_like.png'); expect( - find.descendant( - of: find.byKey(const Key('rail-like')), - matching: find.byIcon(Icons.favorite_rounded), - ), - findsOneWidget, - ); - expect( - find.descendant( - of: find.byKey(const Key('rail-share')), - matching: find.byIcon(Icons.reply_rounded), - ), - findsOneWidget, + railAsset(const Key('rail-share')), + 'assets/icon/rail/rail_share.png', ); expect( tester - .widget( + .widget( find.descendant( of: find.byKey(const Key('rail-share')), - matching: find.byType(Transform), + matching: find.byType(Image), ), ) - .transform - .storage[0], - -1, + .colorBlendMode, + BlendMode.srcIn, ); expect(find.byKey(const Key('progress-loading')), findsOneWidget); expect(find.byKey(const Key('home-feed-progress-thumb')), findsNothing); diff --git a/test/player/player_fullscreen_test.dart b/test/player/player_fullscreen_test.dart index 14b6b1b..d47ee87 100644 --- a/test/player/player_fullscreen_test.dart +++ b/test/player/player_fullscreen_test.dart @@ -474,11 +474,11 @@ void main() { .getSize( find.descendant( of: find.byKey(const Key('player-rail-comments')), - matching: find.byIcon(Icons.chat_bubble_outline_rounded), + matching: find.byType(Image), ), ) .width, - 30, + 32, ); await tester.tap(find.byKey(const Key('player-synopsis-toggle'))); @@ -586,37 +586,31 @@ void main() { await _pumpApp(tester); await _openSeriesPlayer(tester); - Finder railIcon(Key key, IconData icon) => - find.descendant(of: find.byKey(key), matching: find.byIcon(icon)); expect( - railIcon(const Key('player-rail-save'), Icons.star_rounded), - findsOneWidget, + _railAssetName(tester, const Key('player-rail-save')), + 'assets/icon/rail/rail_save.png', ); expect( - railIcon(const Key('player-rail-like'), Icons.favorite_rounded), - findsOneWidget, + _railAssetName(tester, const Key('player-rail-comments')), + 'assets/icon/rail/rail_comment.png', ); expect( - railIcon( - const Key('player-rail-comments'), - Icons.chat_bubble_outline_rounded, - ), - findsOneWidget, + _railAssetName(tester, const Key('player-rail-like')), + 'assets/icon/rail/rail_like.png', ); - final shareIcon = railIcon( - const Key('player-rail-share'), - Icons.reply_rounded, + expect( + _railAssetName(tester, const Key('player-rail-share')), + 'assets/icon/rail/rail_share.png', ); - expect(shareIcon, findsOneWidget); - expect(tester.widget(shareIcon).size, 30); - - final mirror = tester.widget( + final shareImage = tester.widget( find.descendant( of: find.byKey(const Key('player-rail-share')), - matching: find.byType(Transform), + matching: find.byType(Image), ), ); - expect(mirror.transform.storage[0], -1); + expect(shareImage.width, 32); + expect(shareImage.height, 32); + expect(shareImage.colorBlendMode, BlendMode.srcIn); final saveButton = tester.widget( find.descendant( @@ -1132,6 +1126,13 @@ void main() { }); } +String _railAssetName(WidgetTester tester, Key itemKey) { + final image = tester.widget( + find.descendant(of: find.byKey(itemKey), matching: find.byType(Image)), + ); + return (image.image as AssetImage).assetName; +} + void _expectPlayerNavigationBar(WidgetTester tester) { final overlay = tester.widget>( find.byKey(const Key('player-system-overlay')).last,