fix(recommend): 更正筛选按钮所属页面
This commit is contained in:
1 parent
bf20e44b85
commit
13d70fcef2
10 files changed
+124
-107
No files matched your search
@@ -1228,3 +1228,4 @@ $ flutter test
|
|||||||
- 首页二级标签行改为纯横向滑动,右侧三横线按钮不渲染也不占位。
|
- 首页二级标签行改为纯横向滑动,右侧三横线按钮不渲染也不占位。
|
||||||
- 删除不再使用的筛选回调、尺寸 token、颜色和 CustomPainter;全部短剧页筛选按钮保持不变。
|
- 删除不再使用的筛选回调、尺寸 token、颜色和 CustomPainter;全部短剧页筛选按钮保持不变。
|
||||||
- `flutter analyze`:No issues found。
|
- `flutter analyze`:No issues found。
|
||||||
|
- 按后续更正恢复首页透明三横线按钮(26、无背景、`#7A7A7E`),删除全部短剧第 4 行圆形按钮及回调链路。
|
||||||
@@ -80,8 +80,6 @@ abstract final class AppColors {
|
|||||||
static const filterChipSelected = Color(0xFF21090E);
|
static const filterChipSelected = Color(0xFF21090E);
|
||||||
static const filterChipTextSelected = Color(0xFFFF4D6A);
|
static const filterChipTextSelected = Color(0xFFFF4D6A);
|
||||||
static const filterChipTextInactive = Color(0xFF8A8A8A);
|
static const filterChipTextInactive = Color(0xFF8A8A8A);
|
||||||
static const filterMenuButton = Color(0xFF2A2A2A);
|
|
||||||
static const filterMenuIcon = Color(0xFFBDBDBD);
|
|
||||||
|
|
||||||
/// 「我的」页卡片与描边。
|
/// 「我的」页卡片与描边。
|
||||||
static const mineCard = Color(0xFF16161A);
|
static const mineCard = Color(0xFF16161A);
|
||||||
@@ -92,6 +90,7 @@ abstract final class AppColors {
|
|||||||
static const textSecondary = Color(0xFF8E8E93);
|
static const textSecondary = Color(0xFF8E8E93);
|
||||||
static const textTertiary = Color(0xFF9E9E9E);
|
static const textTertiary = Color(0xFF9E9E9E);
|
||||||
static const textTabInactive = Color(0xFFB0B0B0);
|
static const textTabInactive = Color(0xFFB0B0B0);
|
||||||
|
static const textFilterIcon = Color(0xFF7A7A7E);
|
||||||
|
|
||||||
/// 原生开屏与冷启动过渡底色。
|
/// 原生开屏与冷启动过渡底色。
|
||||||
static const splashInk = ink;
|
static const splashInk = ink;
|
||||||
|
|||||||
@@ -114,6 +114,10 @@ abstract final class AppDimens {
|
|||||||
static const Duration homeSecondaryTagAnimationDuration = Duration(
|
static const Duration homeSecondaryTagAnimationDuration = Duration(
|
||||||
milliseconds: 150,
|
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 homePrimaryCategoryGap = 24;
|
||||||
static const double homeSecondaryTagGap = 10;
|
static const double homeSecondaryTagGap = 10;
|
||||||
static const double homeSearchTopGap = 0;
|
static const double homeSearchTopGap = 0;
|
||||||
@@ -143,9 +147,6 @@ abstract final class AppDimens {
|
|||||||
static const double allDramasGridGap = 6;
|
static const double allDramasGridGap = 6;
|
||||||
static const double allDramasGridRowGap = 12;
|
static const double allDramasGridRowGap = 12;
|
||||||
static const double allDramasFilterToGridGap = 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 followingTabFont = 16;
|
||||||
static const double followingGridAspectRatio = 0.5;
|
static const double followingGridAspectRatio = 0.5;
|
||||||
|
|
||||||
|
|||||||
@@ -276,6 +276,7 @@ class _HomeTabsState extends State<_HomeTabs> {
|
|||||||
onSearch: () => _openSearch(context),
|
onSearch: () => _openSearch(context),
|
||||||
onOpenVip: () => unawaited(_openVip(context)),
|
onOpenVip: () => unawaited(_openVip(context)),
|
||||||
onOpenWallet: () => unawaited(_openWallet(context)),
|
onOpenWallet: () => unawaited(_openWallet(context)),
|
||||||
|
onOpenFilter: () => _pushPage(context, const FilterPage()),
|
||||||
),
|
),
|
||||||
onOpenDrama: (drama) => unawaited(logic.playDrama(context, drama)),
|
onOpenDrama: (drama) => unawaited(logic.playDrama(context, drama)),
|
||||||
);
|
);
|
||||||
@@ -286,7 +287,6 @@ class _HomeTabsState extends State<_HomeTabs> {
|
|||||||
feedController: widget.feedController,
|
feedController: widget.feedController,
|
||||||
active: logic.tab == 1,
|
active: logic.tab == 1,
|
||||||
onSearch: () => _openSearch(context),
|
onSearch: () => _openSearch(context),
|
||||||
onOpenFilter: () => _pushPage(context, const FilterPage()),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,13 @@ class HomeGridCallbacks {
|
|||||||
required this.onSearch,
|
required this.onSearch,
|
||||||
required this.onOpenVip,
|
required this.onOpenVip,
|
||||||
required this.onOpenWallet,
|
required this.onOpenWallet,
|
||||||
|
required this.onOpenFilter,
|
||||||
});
|
});
|
||||||
|
|
||||||
final VoidCallback onSearch;
|
final VoidCallback onSearch;
|
||||||
final VoidCallback onOpenVip;
|
final VoidCallback onOpenVip;
|
||||||
final VoidCallback onOpenWallet;
|
final VoidCallback onOpenWallet;
|
||||||
|
final VoidCallback onOpenFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
class HomeGridHeader extends StatelessWidget {
|
class HomeGridHeader extends StatelessWidget {
|
||||||
@@ -80,7 +82,7 @@ class HomeGridHeader extends StatelessWidget {
|
|||||||
_searchRow(l10n),
|
_searchRow(l10n),
|
||||||
_primaryRow(l10n),
|
_primaryRow(l10n),
|
||||||
if (selectedPrimary == HomePrimaryTab.featured)
|
if (selectedPrimary == HomePrimaryTab.featured)
|
||||||
_tagRow()
|
_tagRow(l10n)
|
||||||
else
|
else
|
||||||
const SizedBox(height: AppDimens.homeSecondaryGridGap),
|
const SizedBox(height: AppDimens.homeSecondaryGridGap),
|
||||||
],
|
],
|
||||||
@@ -322,7 +324,7 @@ class HomeGridHeader extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _tagRow() {
|
Widget _tagRow(AppLocalizations l10n) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(
|
padding: const EdgeInsets.fromLTRB(
|
||||||
AppDimens.feedPageHorizontal,
|
AppDimens.feedPageHorizontal,
|
||||||
@@ -330,19 +332,54 @@ class HomeGridHeader extends StatelessWidget {
|
|||||||
AppDimens.feedPageHorizontal,
|
AppDimens.feedPageHorizontal,
|
||||||
AppDimens.homeSecondaryGridGap,
|
AppDimens.homeSecondaryGridGap,
|
||||||
),
|
),
|
||||||
child: SingleChildScrollView(
|
child: Row(
|
||||||
key: const Key('home-secondary-tags'),
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
scrollDirection: Axis.horizontal,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
child: Row(
|
children: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
Expanded(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
child: SingleChildScrollView(
|
||||||
children: [
|
key: const Key('home-secondary-tags'),
|
||||||
for (final tag in tags) ...[
|
scrollDirection: Axis.horizontal,
|
||||||
_tagChip(tag),
|
child: Row(
|
||||||
const SizedBox(width: AppDimens.homeSecondaryTagGap),
|
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;
|
||||||
|
}
|
||||||
@@ -24,13 +24,11 @@ class RecommendPage extends StatefulWidget {
|
|||||||
required this.feedController,
|
required this.feedController,
|
||||||
required this.active,
|
required this.active,
|
||||||
required this.onSearch,
|
required this.onSearch,
|
||||||
required this.onOpenFilter,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final HomeFeedController feedController;
|
final HomeFeedController feedController;
|
||||||
final bool active;
|
final bool active;
|
||||||
final VoidCallback onSearch;
|
final VoidCallback onSearch;
|
||||||
final VoidCallback onOpenFilter;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<RecommendPage> createState() => _RecommendPageState();
|
State<RecommendPage> createState() => _RecommendPageState();
|
||||||
@@ -85,7 +83,6 @@ class _RecommendPageState extends State<RecommendPage> {
|
|||||||
AllDramasFilterGrid(
|
AllDramasFilterGrid(
|
||||||
feedController: widget.feedController,
|
feedController: widget.feedController,
|
||||||
onOpenDrama: _openDrama,
|
onOpenDrama: _openDrama,
|
||||||
onOpenFilter: widget.onOpenFilter,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -24,12 +24,10 @@ class AllDramasFilterGrid extends StatefulWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.feedController,
|
required this.feedController,
|
||||||
required this.onOpenDrama,
|
required this.onOpenDrama,
|
||||||
required this.onOpenFilter,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
final HomeFeedController feedController;
|
final HomeFeedController feedController;
|
||||||
final ValueChanged<Drama> onOpenDrama;
|
final ValueChanged<Drama> onOpenDrama;
|
||||||
final VoidCallback onOpenFilter;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AllDramasFilterGrid> createState() => _AllDramasFilterGridState();
|
State<AllDramasFilterGrid> createState() => _AllDramasFilterGridState();
|
||||||
@@ -295,49 +293,21 @@ class _AllDramasFilterGridState extends State<AllDramasFilterGrid> {
|
|||||||
Widget _sortRow(AppLocalizations l10n) {
|
Widget _sortRow(AppLocalizations l10n) {
|
||||||
return _filterRow(
|
return _filterRow(
|
||||||
row: 3,
|
row: 3,
|
||||||
height: AppDimens.allDramasFilterButton,
|
child: _horizontalChipScroll(
|
||||||
child: Row(
|
scrollKey: const Key('all-filter-scroll-3'),
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
chips: [
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
_chip(
|
||||||
children: [
|
key: const Key('all-sort-hot'),
|
||||||
Expanded(
|
label: l10n.hot,
|
||||||
child: _horizontalChipScroll(
|
selected: !_newest,
|
||||||
scrollKey: const Key('all-filter-scroll-3'),
|
onTap: () => setState(() => _newest = false),
|
||||||
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),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: AppDimens.filterItemGap),
|
const SizedBox(width: AppDimens.filterItemGap),
|
||||||
Material(
|
_chip(
|
||||||
key: const Key('all-dramas-filter-page-button'),
|
key: const Key('all-sort-new'),
|
||||||
color: AppColors.filterMenuButton,
|
label: l10n.newest,
|
||||||
shape: const CircleBorder(),
|
selected: _newest,
|
||||||
child: InkWell(
|
onTap: () => setState(() => _newest = true),
|
||||||
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,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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-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-secondary-tags')), findsOneWidget);
|
||||||
|
expect(find.byKey(const Key('home-grid-filter')), findsOneWidget);
|
||||||
expect(
|
expect(
|
||||||
tester.getRect(find.byKey(const Key('home-secondary-tags'))).left,
|
tester.getSize(find.byKey(const Key('home-grid-filter'))),
|
||||||
closeTo(AppDimens.feedPageHorizontal, 0.1),
|
const Size.square(AppDimens.homeSecondaryFilterVisualSize),
|
||||||
);
|
);
|
||||||
expect(
|
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(
|
closeTo(
|
||||||
tester.getRect(find.byKey(const Key('home-grid'))).right -
|
tester.getRect(find.byKey(const Key('home-grid'))).right -
|
||||||
AppDimens.feedPageHorizontal,
|
AppDimens.feedPageHorizontal,
|
||||||
0.1,
|
0.1,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(find.byKey(const Key('home-grid-filter')), findsNothing);
|
final filterIcon = tester.widget<CustomPaint>(
|
||||||
expect(find.byKey(const Key('home-secondary-filter-visual')), findsNothing);
|
find.byKey(const Key('home-secondary-filter-icon')),
|
||||||
expect(find.byKey(const Key('home-secondary-filter-icon')), findsNothing);
|
);
|
||||||
|
expect(
|
||||||
|
filterIcon.size,
|
||||||
|
const Size.square(AppDimens.homeSecondaryFilterIcon),
|
||||||
|
);
|
||||||
|
final filterMaterial = tester.widget<Material>(
|
||||||
|
find
|
||||||
|
.ancestor(
|
||||||
|
of: find.byKey(const Key('home-grid-filter')),
|
||||||
|
matching: find.byType(Material),
|
||||||
|
)
|
||||||
|
.first,
|
||||||
|
);
|
||||||
|
expect(filterMaterial.color, AppColors.transparent);
|
||||||
final searchBox = find.descendant(
|
final searchBox = find.descendant(
|
||||||
of: find.byKey(const Key('home-search-entry')),
|
of: find.byKey(const Key('home-search-entry')),
|
||||||
matching: find.byType(Container),
|
matching: find.byType(Container),
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ void main() {
|
|||||||
body: AllDramasFilterGrid(
|
body: AllDramasFilterGrid(
|
||||||
feedController: controller,
|
feedController: controller,
|
||||||
onOpenDrama: (_) {},
|
onOpenDrama: (_) {},
|
||||||
onOpenFilter: () {},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
expect(
|
expect(
|
||||||
tester.getSize(find.byKey(const Key('all-filter-row-3'))).height,
|
tester.getSize(find.byKey(const Key('all-filter-row-3'))).height,
|
||||||
AppDimens.allDramasFilterButton,
|
AppDimens.filterChipHeight,
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
tester.getRect(find.byKey(const Key('all-dramas-scroll'))).top -
|
tester.getRect(find.byKey(const Key('all-dramas-scroll'))).top -
|
||||||
@@ -437,38 +437,7 @@ void main() {
|
|||||||
(chipContainer.decoration! as BoxDecoration).borderRadius,
|
(chipContainer.decoration! as BoxDecoration).borderRadius,
|
||||||
BorderRadius.circular(AppDimens.filterChipRadius),
|
BorderRadius.circular(AppDimens.filterChipRadius),
|
||||||
);
|
);
|
||||||
final filterButton = find.byKey(const Key('all-dramas-filter-page'));
|
expect(find.byKey(const Key('all-dramas-filter-page')), findsNothing);
|
||||||
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<Material>(
|
|
||||||
find.byKey(const Key('all-dramas-filter-page-button')),
|
|
||||||
);
|
|
||||||
expect(filterMaterial.color, AppColors.filterMenuButton);
|
|
||||||
expect(filterMaterial.shape, const CircleBorder());
|
|
||||||
final filterIcon = tester.widget<Icon>(
|
|
||||||
find.descendant(
|
|
||||||
of: filterButton,
|
|
||||||
matching: find.byIcon(Icons.filter_list_rounded),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
expect(filterIcon.size, AppDimens.allDramasFilterIcon);
|
|
||||||
expect(filterIcon.color, AppColors.filterMenuIcon);
|
|
||||||
|
|
||||||
await tester.tap(find.byKey(const Key('recommend-tab-playlist')));
|
await tester.tap(find.byKey(const Key('recommend-tab-playlist')));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|||||||
Reference in new issue
Block a user