diff --git a/docs/plans/PROGRESS.md b/docs/plans/PROGRESS.md index 1fc6ae2..57a957d 100644 --- a/docs/plans/PROGRESS.md +++ b/docs/plans/PROGRESS.md @@ -1228,3 +1228,4 @@ $ flutter test - 首页二级标签行改为纯横向滑动,右侧三横线按钮不渲染也不占位。 - 删除不再使用的筛选回调、尺寸 token、颜色和 CustomPainter;全部短剧页筛选按钮保持不变。 - `flutter analyze`:No issues found。 +- 按后续更正恢复首页透明三横线按钮(26、无背景、`#7A7A7E`),删除全部短剧第 4 行圆形按钮及回调链路。 diff --git a/lib/core/theme/app_colors.dart b/lib/core/theme/app_colors.dart index c421a1c..4536f27 100644 --- a/lib/core/theme/app_colors.dart +++ b/lib/core/theme/app_colors.dart @@ -80,8 +80,6 @@ abstract final class AppColors { static const filterChipSelected = Color(0xFF21090E); static const filterChipTextSelected = Color(0xFFFF4D6A); static const filterChipTextInactive = Color(0xFF8A8A8A); - static const filterMenuButton = Color(0xFF2A2A2A); - static const filterMenuIcon = Color(0xFFBDBDBD); /// 「我的」页卡片与描边。 static const mineCard = Color(0xFF16161A); @@ -92,6 +90,7 @@ abstract final class AppColors { static const textSecondary = Color(0xFF8E8E93); static const textTertiary = Color(0xFF9E9E9E); static const textTabInactive = Color(0xFFB0B0B0); + static const textFilterIcon = Color(0xFF7A7A7E); /// 原生开屏与冷启动过渡底色。 static const splashInk = ink; diff --git a/lib/core/theme/app_dimens.dart b/lib/core/theme/app_dimens.dart index e233bdb..e8ce8a1 100644 --- a/lib/core/theme/app_dimens.dart +++ b/lib/core/theme/app_dimens.dart @@ -114,6 +114,10 @@ abstract final class AppDimens { static const Duration homeSecondaryTagAnimationDuration = Duration( milliseconds: 150, ); + static const double homeSecondaryFilterGap = 6; + static const double homeSecondaryFilterVisualSize = 26; + static const double homeSecondaryFilterIcon = 16; + static const double homeSecondaryFilterStroke = 1.7; static const double homePrimaryCategoryGap = 24; static const double homeSecondaryTagGap = 10; static const double homeSearchTopGap = 0; @@ -143,9 +147,6 @@ abstract final class AppDimens { static const double allDramasGridGap = 6; static const double allDramasGridRowGap = 12; static const double allDramasFilterToGridGap = 12; - static const double allDramasFilterIcon = 22; - static const double allDramasFilterButton = 44; - static const double allDramasFilterRight = 12; static const double followingTabFont = 16; static const double followingGridAspectRatio = 0.5; diff --git a/lib/pages/home/home_view.dart b/lib/pages/home/home_view.dart index ee2445e..46db787 100644 --- a/lib/pages/home/home_view.dart +++ b/lib/pages/home/home_view.dart @@ -276,6 +276,7 @@ class _HomeTabsState extends State<_HomeTabs> { onSearch: () => _openSearch(context), onOpenVip: () => unawaited(_openVip(context)), onOpenWallet: () => unawaited(_openWallet(context)), + onOpenFilter: () => _pushPage(context, const FilterPage()), ), onOpenDrama: (drama) => unawaited(logic.playDrama(context, drama)), ); @@ -286,7 +287,6 @@ class _HomeTabsState extends State<_HomeTabs> { feedController: widget.feedController, active: logic.tab == 1, onSearch: () => _openSearch(context), - onOpenFilter: () => _pushPage(context, const FilterPage()), ); } diff --git a/lib/pages/home/widgets/home_grid_header.dart b/lib/pages/home/widgets/home_grid_header.dart index 78710d3..4c6ef5e 100644 --- a/lib/pages/home/widgets/home_grid_header.dart +++ b/lib/pages/home/widgets/home_grid_header.dart @@ -40,11 +40,13 @@ class HomeGridCallbacks { required this.onSearch, required this.onOpenVip, required this.onOpenWallet, + required this.onOpenFilter, }); final VoidCallback onSearch; final VoidCallback onOpenVip; final VoidCallback onOpenWallet; + final VoidCallback onOpenFilter; } class HomeGridHeader extends StatelessWidget { @@ -80,7 +82,7 @@ class HomeGridHeader extends StatelessWidget { _searchRow(l10n), _primaryRow(l10n), if (selectedPrimary == HomePrimaryTab.featured) - _tagRow() + _tagRow(l10n) else const SizedBox(height: AppDimens.homeSecondaryGridGap), ], @@ -322,7 +324,7 @@ class HomeGridHeader extends StatelessWidget { ); } - Widget _tagRow() { + Widget _tagRow(AppLocalizations l10n) { return Padding( padding: const EdgeInsets.fromLTRB( AppDimens.feedPageHorizontal, @@ -330,19 +332,54 @@ class HomeGridHeader extends StatelessWidget { AppDimens.feedPageHorizontal, AppDimens.homeSecondaryGridGap, ), - child: SingleChildScrollView( - key: const Key('home-secondary-tags'), - scrollDirection: Axis.horizontal, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - for (final tag in tags) ...[ - _tagChip(tag), - const SizedBox(width: AppDimens.homeSecondaryTagGap), - ], - ], - ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded( + child: SingleChildScrollView( + key: const Key('home-secondary-tags'), + scrollDirection: Axis.horizontal, + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + for (final tag in tags) ...[ + _tagChip(tag), + const SizedBox(width: AppDimens.homeSecondaryTagGap), + ], + ], + ), + ), + ), + const SizedBox(width: AppDimens.homeSecondaryFilterGap), + Tooltip( + message: l10n.filter, + child: SizedBox.square( + dimension: AppDimens.homeSecondaryFilterVisualSize, + child: Material( + color: AppColors.transparent, + child: InkWell( + key: const Key('home-grid-filter'), + onTap: callbacks.onOpenFilter, + child: Center( + child: SizedBox.square( + key: const Key('home-secondary-filter-visual'), + dimension: AppDimens.homeSecondaryFilterVisualSize, + child: const Center( + child: CustomPaint( + key: Key('home-secondary-filter-icon'), + size: Size.square(AppDimens.homeSecondaryFilterIcon), + painter: _ThreeLineFilterPainter(), + ), + ), + ), + ), + ), + ), + ), + ), + ], ), ); } @@ -380,3 +417,25 @@ class HomeGridHeader extends StatelessWidget { ); } } + +class _ThreeLineFilterPainter extends CustomPainter { + const _ThreeLineFilterPainter(); + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint() + ..color = AppColors.textFilterIcon + ..strokeWidth = AppDimens.homeSecondaryFilterStroke + ..strokeCap = StrokeCap.butt; + for (final y in [ + size.height * 0.25, + size.height * 0.5, + size.height * 0.75, + ]) { + canvas.drawLine(Offset(0, y), Offset(size.width, y), paint); + } + } + + @override + bool shouldRepaint(_ThreeLineFilterPainter oldDelegate) => false; +} diff --git a/lib/pages/recommend/recommend_page.dart b/lib/pages/recommend/recommend_page.dart index 2d054c6..3734263 100644 --- a/lib/pages/recommend/recommend_page.dart +++ b/lib/pages/recommend/recommend_page.dart @@ -24,13 +24,11 @@ class RecommendPage extends StatefulWidget { required this.feedController, required this.active, required this.onSearch, - required this.onOpenFilter, }); final HomeFeedController feedController; final bool active; final VoidCallback onSearch; - final VoidCallback onOpenFilter; @override State createState() => _RecommendPageState(); @@ -85,7 +83,6 @@ class _RecommendPageState extends State { AllDramasFilterGrid( feedController: widget.feedController, onOpenDrama: _openDrama, - onOpenFilter: widget.onOpenFilter, ), ), ), diff --git a/lib/pages/recommend/widgets/all_dramas_filter_grid.dart b/lib/pages/recommend/widgets/all_dramas_filter_grid.dart index 245f8af..0440a1f 100644 --- a/lib/pages/recommend/widgets/all_dramas_filter_grid.dart +++ b/lib/pages/recommend/widgets/all_dramas_filter_grid.dart @@ -24,12 +24,10 @@ class AllDramasFilterGrid extends StatefulWidget { super.key, required this.feedController, required this.onOpenDrama, - required this.onOpenFilter, }); final HomeFeedController feedController; final ValueChanged onOpenDrama; - final VoidCallback onOpenFilter; @override State createState() => _AllDramasFilterGridState(); @@ -295,49 +293,21 @@ class _AllDramasFilterGridState extends State { Widget _sortRow(AppLocalizations l10n) { return _filterRow( row: 3, - height: AppDimens.allDramasFilterButton, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Expanded( - child: _horizontalChipScroll( - scrollKey: const Key('all-filter-scroll-3'), - chips: [ - _chip( - key: const Key('all-sort-hot'), - label: l10n.hot, - selected: !_newest, - onTap: () => setState(() => _newest = false), - ), - const SizedBox(width: AppDimens.filterItemGap), - _chip( - key: const Key('all-sort-new'), - label: l10n.newest, - selected: _newest, - onTap: () => setState(() => _newest = true), - ), - ], - ), + child: _horizontalChipScroll( + scrollKey: const Key('all-filter-scroll-3'), + chips: [ + _chip( + key: const Key('all-sort-hot'), + label: l10n.hot, + selected: !_newest, + onTap: () => setState(() => _newest = false), ), const SizedBox(width: AppDimens.filterItemGap), - Material( - key: const Key('all-dramas-filter-page-button'), - color: AppColors.filterMenuButton, - shape: const CircleBorder(), - child: InkWell( - key: const Key('all-dramas-filter-page'), - customBorder: const CircleBorder(), - onTap: widget.onOpenFilter, - child: const SizedBox.square( - dimension: AppDimens.allDramasFilterButton, - child: Icon( - Icons.filter_list_rounded, - size: AppDimens.allDramasFilterIcon, - color: AppColors.filterMenuIcon, - ), - ), - ), + _chip( + key: const Key('all-sort-new'), + label: l10n.newest, + selected: _newest, + onTap: () => setState(() => _newest = true), ), ], ), diff --git a/test/home/home_preview_layout_test.dart b/test/home/home_preview_layout_test.dart index f9566de..5bd0f70 100644 --- a/test/home/home_preview_layout_test.dart +++ b/test/home/home_preview_layout_test.dart @@ -94,21 +94,43 @@ void main() { expect(find.byKey(const Key('home-section-category-古装')), findsOneWidget); expect(find.byKey(const Key('home-section-category-社群')), findsOneWidget); expect(find.byKey(const Key('home-secondary-tags')), findsOneWidget); + expect(find.byKey(const Key('home-grid-filter')), findsOneWidget); expect( - tester.getRect(find.byKey(const Key('home-secondary-tags'))).left, - closeTo(AppDimens.feedPageHorizontal, 0.1), + tester.getSize(find.byKey(const Key('home-grid-filter'))), + const Size.square(AppDimens.homeSecondaryFilterVisualSize), ); expect( - tester.getRect(find.byKey(const Key('home-secondary-tags'))).right, + tester.getSize(find.byKey(const Key('home-secondary-filter-visual'))), + const Size.square(AppDimens.homeSecondaryFilterVisualSize), + ); + expect( + tester.getCenter(find.byKey(const Key('home-grid-filter'))).dy, + closeTo(tester.getCenter(find.byKey(const Key('home-tag-hot'))).dy, 0.1), + ); + expect( + tester.getRect(find.byKey(const Key('home-grid-filter'))).right, closeTo( tester.getRect(find.byKey(const Key('home-grid'))).right - AppDimens.feedPageHorizontal, 0.1, ), ); - expect(find.byKey(const Key('home-grid-filter')), findsNothing); - expect(find.byKey(const Key('home-secondary-filter-visual')), findsNothing); - expect(find.byKey(const Key('home-secondary-filter-icon')), findsNothing); + final filterIcon = tester.widget( + find.byKey(const Key('home-secondary-filter-icon')), + ); + expect( + filterIcon.size, + const Size.square(AppDimens.homeSecondaryFilterIcon), + ); + final filterMaterial = tester.widget( + find + .ancestor( + of: find.byKey(const Key('home-grid-filter')), + matching: find.byType(Material), + ) + .first, + ); + expect(filterMaterial.color, AppColors.transparent); final searchBox = find.descendant( of: find.byKey(const Key('home-search-entry')), matching: find.byType(Container), diff --git a/test/recommend/all_dramas_filter_alignment_test.dart b/test/recommend/all_dramas_filter_alignment_test.dart index 4f35d15..000aa1b 100644 --- a/test/recommend/all_dramas_filter_alignment_test.dart +++ b/test/recommend/all_dramas_filter_alignment_test.dart @@ -25,7 +25,6 @@ void main() { body: AllDramasFilterGrid( feedController: controller, onOpenDrama: (_) {}, - onOpenFilter: () {}, ), ), ), diff --git a/test/recommend/recommend_sections_test.dart b/test/recommend/recommend_sections_test.dart index 2503f52..8acd660 100644 --- a/test/recommend/recommend_sections_test.dart +++ b/test/recommend/recommend_sections_test.dart @@ -309,7 +309,7 @@ void main() { } expect( tester.getSize(find.byKey(const Key('all-filter-row-3'))).height, - AppDimens.allDramasFilterButton, + AppDimens.filterChipHeight, ); expect( tester.getRect(find.byKey(const Key('all-dramas-scroll'))).top - @@ -437,38 +437,7 @@ void main() { (chipContainer.decoration! as BoxDecoration).borderRadius, BorderRadius.circular(AppDimens.filterChipRadius), ); - final filterButton = find.byKey(const Key('all-dramas-filter-page')); - expect( - find.descendant( - of: find.byKey(const Key('all-filter-scroll-3')), - matching: filterButton, - ), - findsNothing, - ); - expect( - tester.getSize(filterButton), - const Size.square(AppDimens.allDramasFilterButton), - ); - expect( - tester.getRect(filterButton).right, - closeTo( - tester.getRect(find.byKey(const Key('all-filter-row-3'))).right, - 0.1, - ), - ); - final filterMaterial = tester.widget( - find.byKey(const Key('all-dramas-filter-page-button')), - ); - expect(filterMaterial.color, AppColors.filterMenuButton); - expect(filterMaterial.shape, const CircleBorder()); - final filterIcon = tester.widget( - find.descendant( - of: filterButton, - matching: find.byIcon(Icons.filter_list_rounded), - ), - ); - expect(filterIcon.size, AppDimens.allDramasFilterIcon); - expect(filterIcon.color, AppColors.filterMenuIcon); + expect(find.byKey(const Key('all-dramas-filter-page')), findsNothing); await tester.tap(find.byKey(const Key('recommend-tab-playlist'))); await tester.pumpAndSettle();