From c1dd0335812a24d1a16ae62dbbd2d79998a414ce Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Tue, 20 Oct 2026 16:15:53 +0800 Subject: [PATCH] =?UTF-8?q?fix(flutter):=20=E7=BB=9F=E4=B8=80=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E9=A1=B6=E9=83=A8=E5=9C=86=E5=BD=A2=E6=8C=89?= =?UTF-8?q?=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/widgets/subpage_header.dart | 51 ++++++++++++++---- lib/pages/drama_detail/drama_detail_view.dart | 54 ++++++++++++++----- .../widgets/drama_detail_header.dart | 40 ++------------ test/drama_detail/drama_detail_view_test.dart | 14 +++++ 4 files changed, 97 insertions(+), 62 deletions(-) diff --git a/lib/core/widgets/subpage_header.dart b/lib/core/widgets/subpage_header.dart index ac44985..6924433 100644 --- a/lib/core/widgets/subpage_header.dart +++ b/lib/core/widgets/subpage_header.dart @@ -21,27 +21,31 @@ const Color _darkBackground = Color(0x59000000); const Color _darkShadowColor = Color(0x33000000); const Offset _lightShadowOffset = Offset(0, 2); -/// 二级页面统一返回按钮,浅色页使用白底描边,深色页使用半透明黑底。 -class AppSubpageBackButton extends StatelessWidget { - const AppSubpageBackButton({ +/// 二级页面统一圆形操作按钮,返回、分享和更多共用同一尺寸与表面样式。 +class AppSubpageCircleButton extends StatelessWidget { + const AppSubpageCircleButton({ super.key, - required this.backKey, + required this.buttonKey, + required this.tooltip, + required this.icon, + required this.onPressed, this.dark = false, - this.onBack, }); - final Key backKey; + final Key buttonKey; + final String tooltip; + final IconData icon; + final VoidCallback onPressed; final bool dark; - final VoidCallback? onBack; @override Widget build(BuildContext context) { return SizedBox( - key: backKey, + key: buttonKey, width: _backSize, height: _backSize, child: Tooltip( - message: MaterialLocalizations.of(context).backButtonTooltip, + message: tooltip, child: DecoratedBox( key: const Key('subpage-back-shadow'), decoration: BoxDecoration( @@ -67,10 +71,10 @@ class AppSubpageBackButton extends StatelessWidget { clipBehavior: Clip.antiAlias, child: InkWell( customBorder: const CircleBorder(), - onTap: onBack ?? () => Navigator.of(context).maybePop(), + onTap: onPressed, child: Center( child: Icon( - Icons.chevron_left_rounded, + icon, color: dark ? Colors.white : AppColors.onSurface, size: _iconSize, weight: _iconWeight, @@ -84,6 +88,31 @@ class AppSubpageBackButton extends StatelessWidget { } } +/// 二级页面统一返回按钮,浅色页使用白底描边,深色页使用半透明黑底。 +class AppSubpageBackButton extends StatelessWidget { + const AppSubpageBackButton({ + super.key, + required this.backKey, + this.dark = false, + this.onBack, + }); + + final Key backKey; + final bool dark; + final VoidCallback? onBack; + + @override + Widget build(BuildContext context) { + return AppSubpageCircleButton( + buttonKey: backKey, + tooltip: MaterialLocalizations.of(context).backButtonTooltip, + icon: Icons.chevron_left_rounded, + dark: dark, + onPressed: onBack ?? () => Navigator.of(context).maybePop(), + ); + } +} + /// 二级页面统一页头:返回按钮、左对齐标题、可选副标题与右侧操作。 class AppSubpageHeader extends StatelessWidget { const AppSubpageHeader({ diff --git a/lib/pages/drama_detail/drama_detail_view.dart b/lib/pages/drama_detail/drama_detail_view.dart index f9a9a06..4248772 100644 --- a/lib/pages/drama_detail/drama_detail_view.dart +++ b/lib/pages/drama_detail/drama_detail_view.dart @@ -34,8 +34,10 @@ const String _menuDownload = 'download'; const String _menuShare = 'share'; const String _menuReport = 'report'; const int _maxTags = 4; -const double _moreSize = 36; -const double _moreIconSize = 18; +const double _stateHeaderHeight = 52; +const double _stateHeaderHorizontal = 14; +const double _stateTitleGap = 12; +const double _stateTitleSize = 18; final RegExp _tagSplitter = RegExp(r'[,/、·]'); @@ -189,7 +191,35 @@ class _DramaDetailPageState extends State { body: SafeArea( child: Column( children: [ - AppSubpageHeader(title: title, backKey: const Key('detail-back')), + SizedBox( + height: _stateHeaderHeight, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: _stateHeaderHorizontal, + ), + child: Row( + children: [ + const AppSubpageBackButton( + backKey: Key('detail-back'), + dark: true, + ), + const SizedBox(width: _stateTitleGap), + Expanded( + child: Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: AppColors.onSurface, + fontSize: _stateTitleSize, + fontWeight: FontWeight.w800, + ), + ), + ), + ], + ), + ), + ), Expanded(child: child), ], ), @@ -285,17 +315,13 @@ class _DramaDetailPageState extends State { ), ), ], - child: Container( - width: _moreSize, - height: _moreSize, - decoration: const BoxDecoration( - color: AppColors.card, - shape: BoxShape.circle, - ), - child: const Icon( - Icons.more_horiz_rounded, - size: _moreIconSize, - color: AppColors.onSurface, + child: IgnorePointer( + child: AppSubpageCircleButton( + buttonKey: const Key('detail-more-visual'), + tooltip: l10n.more, + icon: Icons.more_horiz_rounded, + dark: true, + onPressed: () {}, ), ), ); diff --git a/lib/pages/drama_detail/widgets/drama_detail_header.dart b/lib/pages/drama_detail/widgets/drama_detail_header.dart index 833893b..d1da929 100644 --- a/lib/pages/drama_detail/widgets/drama_detail_header.dart +++ b/lib/pages/drama_detail/widgets/drama_detail_header.dart @@ -18,8 +18,6 @@ const double _infoGap = 14; const double _favoriteHeight = 34; const double _favoriteRadius = 17; const double _favoriteHorizontal = 12; -const double _circleSize = 36; -const double _iconSize = 18; /// 详情页顶部操作与短剧主信息所需的交互回调。 class DramaDetailHeaderActions { @@ -87,10 +85,11 @@ class DramaDetailHeader extends StatelessWidget { onBack: actions.onBack, ), const Spacer(), - _CircleAction( - actionKey: const Key('detail-share'), + AppSubpageCircleButton( + buttonKey: const Key('detail-share'), tooltip: l10n.share, icon: Icons.share_outlined, + dark: true, onPressed: actions.onShare, ), const SizedBox(width: 8), @@ -201,36 +200,3 @@ class DramaDetailHeader extends StatelessWidget { ); } } - -class _CircleAction extends StatelessWidget { - const _CircleAction({ - required this.actionKey, - required this.tooltip, - required this.icon, - required this.onPressed, - }); - - final Key actionKey; - final String tooltip; - final IconData icon; - final VoidCallback onPressed; - - @override - Widget build(BuildContext context) { - return Container( - key: actionKey, - width: _circleSize, - height: _circleSize, - decoration: const BoxDecoration( - color: AppColors.card, - shape: BoxShape.circle, - ), - child: IconButton( - tooltip: tooltip, - onPressed: onPressed, - padding: EdgeInsets.zero, - icon: Icon(icon, size: _iconSize, color: AppColors.onSurface), - ), - ); - } -} diff --git a/test/drama_detail/drama_detail_view_test.dart b/test/drama_detail/drama_detail_view_test.dart index 9300e08..5fe78a8 100644 --- a/test/drama_detail/drama_detail_view_test.dart +++ b/test/drama_detail/drama_detail_view_test.dart @@ -94,6 +94,20 @@ void main() { expect(tester.getSize(find.byKey(const Key('detail-cta'))).height, 46); expect(find.byKey(const Key('detail-share')), findsOneWidget); expect(find.byKey(const Key('detail-more')), findsOneWidget); + for (final key in const [Key('detail-share'), Key('detail-more')]) { + expect(tester.getSize(find.byKey(key)), const Size(40, 40)); + expect( + tester + .widget( + find.descendant( + of: find.byKey(key), + matching: find.byType(Material), + ), + ) + .color, + const Color(0x59000000), + ); + } await tester.tap(find.byKey(const Key('detail-more'))); await tester.pumpAndSettle();