feat(home): 顶部导航随翻页联动并把推荐更名为精选

This commit is contained in:
Vicky authored and trae-solo-agent committed 2026-10-30 22:53:15 +08:00
1 parent 2f4593a46e
commit 335e5dc286
27 files changed
+1095 -214

No files matched your search

+110
View File
@@ -0,0 +1,110 @@
// Author: fruit.com.cn
// 职责:验证顶部标签双向过渡、固定布局和减少动画行为。
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fruitshorts/core/theme/app_colors.dart';
import 'package:fruitshorts/core/widgets/animated_nav_label.dart';
const _halfDuration = Duration(milliseconds: 90);
const _duration = Duration(milliseconds: 180);
const _style = TextStyle(fontSize: 20, color: AppColors.navSelected);
const _firstKey = Key('first');
const _secondKey = Key('second');
Widget _labels(bool firstSelected, {bool reduceMotion = false}) {
return MaterialApp(
home: MediaQuery(
data: MediaQueryData(
devicePixelRatio: 3,
disableAnimations: reduceMotion,
),
child: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
AnimatedNavLabel(
key: _firstKey,
label: '精选 Featured',
selected: firstSelected,
style: _style,
),
AnimatedNavLabel(
key: _secondKey,
label: '推荐 Rekomendasi',
selected: !firstSelected,
style: _style,
),
],
),
),
),
);
}
double _scale(WidgetTester tester, Key key) {
final transform = tester.widget<Transform>(
find.descendant(of: find.byKey(key), matching: find.byType(Transform)),
);
return transform.transform.entry(0, 0);
}
Color _color(WidgetTester tester, Key key) {
return tester
.widget<Text>(
find.descendant(of: find.byKey(key), matching: find.byType(Text)),
)
.style!
.color!;
}
void main() {
testWidgets('should_cross_animate_without_relayout_when_selection_changes', (
tester,
) async {
await tester.pumpWidget(_labels(true));
final firstRect = tester.getRect(find.byKey(_firstKey));
final secondRect = tester.getRect(find.byKey(_secondKey));
await tester.pumpWidget(_labels(false));
await tester.pump(_halfDuration);
expect(_scale(tester, _firstKey), inExclusiveRange(0.9, 1.0));
expect(_scale(tester, _secondKey), inExclusiveRange(0.9, 1.0));
expect(_color(tester, _firstKey), isNot(AppColors.navSelected));
expect(_color(tester, _firstKey), isNot(AppColors.textTabInactive));
expect(_color(tester, _secondKey), isNot(AppColors.navSelected));
expect(_color(tester, _secondKey), isNot(AppColors.textTabInactive));
expect(tester.getRect(find.byKey(_firstKey)), firstRect);
expect(tester.getRect(find.byKey(_secondKey)), secondRect);
await tester.pump(_duration);
expect(_scale(tester, _firstKey), closeTo(0.9, 0.0001));
expect(_scale(tester, _secondKey), 1);
expect(_color(tester, _secondKey), AppColors.navSelected);
expect(tester.getRect(find.byKey(_secondKey)), secondRect);
});
testWidgets('should_continue_from_current_value_when_selection_reverses', (
tester,
) async {
await tester.pumpWidget(_labels(true));
await tester.pumpWidget(_labels(false));
await tester.pump(_halfDuration);
final scale = _scale(tester, _firstKey);
final color = _color(tester, _firstKey);
await tester.pumpWidget(_labels(true));
expect(_scale(tester, _firstKey), scale);
expect(_color(tester, _firstKey), color);
await tester.pump(_duration);
expect(_scale(tester, _firstKey), 1);
});
testWidgets('should_skip_transition_when_reduce_motion_is_enabled', (
tester,
) async {
await tester.pumpWidget(_labels(true, reduceMotion: true));
await tester.pumpWidget(_labels(false, reduceMotion: true));
await tester.pump();
expect(_scale(tester, _firstKey), closeTo(0.9, 0.0001));
expect(_color(tester, _secondKey), AppColors.navSelected);
expect(tester.binding.hasScheduledFrame, isFalse);
});
}