merge: 更正筛选按钮所属页面
This commit is contained in:
commit
cffb68809c
10 files changed
+124
-107
No files matched your search
@@ -1228,3 +1228,4 @@ $ flutter test
|
||||
- 首页二级标签行改为纯横向滑动,右侧三横线按钮不渲染也不占位。
|
||||
- 删除不再使用的筛选回调、尺寸 token、颜色和 CustomPainter;全部短剧页筛选按钮保持不变。
|
||||
- `flutter analyze`:No issues found。
|
||||
- 按后续更正恢复首页透明三横线按钮(26、无背景、`#7A7A7E`),删除全部短剧第 4 行圆形按钮及回调链路。
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<RecommendPage> createState() => _RecommendPageState();
|
||||
@@ -85,7 +83,6 @@ class _RecommendPageState extends State<RecommendPage> {
|
||||
AllDramasFilterGrid(
|
||||
feedController: widget.feedController,
|
||||
onOpenDrama: _openDrama,
|
||||
onOpenFilter: widget.onOpenFilter,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -24,12 +24,10 @@ class AllDramasFilterGrid extends StatefulWidget {
|
||||
super.key,
|
||||
required this.feedController,
|
||||
required this.onOpenDrama,
|
||||
required this.onOpenFilter,
|
||||
});
|
||||
|
||||
final HomeFeedController feedController;
|
||||
final ValueChanged<Drama> onOpenDrama;
|
||||
final VoidCallback onOpenFilter;
|
||||
|
||||
@override
|
||||
State<AllDramasFilterGrid> createState() => _AllDramasFilterGridState();
|
||||
@@ -295,49 +293,21 @@ class _AllDramasFilterGridState extends State<AllDramasFilterGrid> {
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -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<CustomPaint>(
|
||||
find.byKey(const Key('home-secondary-filter-icon')),
|
||||
);
|
||||
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(
|
||||
of: find.byKey(const Key('home-search-entry')),
|
||||
matching: find.byType(Container),
|
||||
|
||||
@@ -25,7 +25,6 @@ void main() {
|
||||
body: AllDramasFilterGrid(
|
||||
feedController: controller,
|
||||
onOpenDrama: (_) {},
|
||||
onOpenFilter: () {},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -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<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);
|
||||
expect(find.byKey(const Key('all-dramas-filter-page')), findsNothing);
|
||||
|
||||
await tester.tap(find.byKey(const Key('recommend-tab-playlist')));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
Reference in new issue
Block a user