feat(ui): 重做三 Tab 首页与发现页

This commit is contained in:
Zhou Weidong committed 2026-10-19 15:01:45 +08:00
1 parent eae1c533ce
commit e8c1859e70
23 files changed
+1734 -432

No files matched your search

+85 -28
View File
@@ -7,6 +7,7 @@ import 'package:fruitshorts/app.dart';
import 'package:fruitshorts/core/data/models/content_category.dart';
import 'package:fruitshorts/core/data/models/drama.dart';
import 'package:fruitshorts/core/data/models/home_feed.dart';
import 'package:fruitshorts/core/data/models/watch_progress.dart';
import 'package:fruitshorts/core/data/repositories/category_repository.dart';
import 'package:fruitshorts/core/data/repositories/favorite_repository.dart';
import 'package:fruitshorts/core/data/repositories/playback_repository.dart';
@@ -25,7 +26,7 @@ void main() {
});
testWidgets(
'should_show_all_dramas_in_three_column_sliver_grid_without_continue_watching',
'should_show_all_dramas_in_two_column_sliver_grid_without_continue_watching',
(tester) async {
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
@@ -44,27 +45,15 @@ void main() {
tester.getCenter(find.text('All')).dx,
lessThan(tester.getCenter(find.text('Hot')).dx),
);
expect(
tester
.getTopLeft(
find.byKey(const Key('home-drama-card-the-signal-demo')),
)
.dy -
tester
.getBottomLeft(find.byKey(const Key('home-category-chips')))
.dy,
closeTo(14, 0.1),
);
final grid = tester.widget<SliverGrid>(
find.byKey(const Key('home-all-dramas-grid')),
);
final delegate =
grid.gridDelegate as SliverGridDelegateWithFixedCrossAxisCount;
expect(delegate.crossAxisCount, 3);
expect(delegate.crossAxisSpacing, 10);
expect(delegate.crossAxisCount, 2);
expect(delegate.crossAxisSpacing, 12);
expect(delegate.mainAxisSpacing, 14);
expect(delegate.childAspectRatio, closeTo(2 / 3, 0.001));
expect(delegate.childAspectRatio, closeTo(0.55, 0.001));
expect(
tester
.widget<SliverPadding>(
@@ -80,7 +69,7 @@ void main() {
final cover = tester.widget<AspectRatio>(
find.byKey(const Key('home-drama-cover-city-lights-demo')),
);
expect(cover.aspectRatio, closeTo(2 / 3, 0.001));
expect(cover.aspectRatio, closeTo(3 / 4.2, 0.001));
expect(
tester
.widget<Text>(
@@ -148,15 +137,7 @@ void main() {
expect(find.byKey(const Key('home-all-dramas-title')), findsNothing);
expect(find.text('No matching dramas.'), findsOneWidget);
final content = tester.getCenter(
find.byKey(const Key('empty-state-content')),
);
final chipsBottom = tester
.getBottomLeft(find.byKey(const Key('home-category-chips')))
.dy;
final feed = tester.getRect(find.byType(CustomScrollView));
expect(content.dx, closeTo(feed.center.dx, 1));
expect(content.dy, closeTo((chipsBottom + feed.bottom) / 2, 36));
expect(find.byKey(const Key('empty-state-content')), findsOneWidget);
await tester.tap(find.text('Romance'));
await tester.pumpAndSettle();
@@ -184,7 +165,7 @@ void main() {
expect(find.byKey(const Key('home-all-dramas-grid')), findsNothing);
});
testWidgets('should_fit_three_column_grid_on_compact_screen', (tester) async {
testWidgets('should_fit_two_column_grid_on_compact_screen', (tester) async {
tester.view.physicalSize = const Size(320, 568);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
@@ -204,11 +185,87 @@ void main() {
expect(find.text('City Lights & Velvet Nights'), findsOneWidget);
expect(tester.takeException(), isNull);
});
testWidgets('should_filter_trending_grid_when_search_text_changes', (
tester,
) async {
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await _pumpApp(tester, categoryRepository: _GridCategoryRepository());
await tester.tap(find.byKey(const Key('tab-discover')));
await tester.pumpAndSettle();
expect(
find.byKey(const Key('home-drama-card-the-signal-demo')),
findsOneWidget,
);
expect(
find.byKey(const Key('home-drama-card-city-lights-demo')),
findsOneWidget,
);
await tester.enterText(find.byKey(const Key('discover-search')), 'velvet');
await tester.pumpAndSettle();
expect(
find.byKey(const Key('home-drama-card-the-signal-demo')),
findsNothing,
);
expect(
find.byKey(const Key('home-drama-card-city-lights-demo')),
findsOneWidget,
);
await tester.enterText(find.byKey(const Key('discover-search')), '');
await tester.pumpAndSettle();
expect(
find.byKey(const Key('home-drama-card-the-signal-demo')),
findsOneWidget,
);
});
testWidgets('should_show_continue_watching_only_when_a_record_exists', (
tester,
) async {
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await _pumpApp(
tester,
categoryRepository: _GridCategoryRepository(),
history: InMemoryWatchHistoryRepository(
seed: const [
WatchProgress(
dramaId: 'city-lights-demo',
episodeId: 'city-lights-demo-1',
position: Duration(seconds: 42),
duration: Duration(minutes: 2),
),
],
),
);
await tester.tap(find.byKey(const Key('tab-discover')));
await tester.pumpAndSettle();
expect(find.byKey(const Key('discover-continue-watching')), findsOneWidget);
expect(find.byKey(const Key('discover-continue-play')), findsOneWidget);
expect(find.text('City Lights & Velvet Nights'), findsWidgets);
expect(find.byKey(const Key('discover-quick-filter')), findsOneWidget);
expect(find.byKey(const Key('discover-quick-charts')), findsOneWidget);
expect(find.byKey(const Key('discover-quick-new')), findsOneWidget);
expect(find.byKey(const Key('discover-quick-watchlist')), findsOneWidget);
});
}
Future<void> _pumpApp(
WidgetTester tester, {
required CategoryRepository categoryRepository,
WatchHistoryRepository? history,
}) async {
final catalog = FakeCatalogRepository();
await tester.pumpWidget(
@@ -216,7 +273,7 @@ Future<void> _pumpApp(
playerFactory: createWidgetPlayer,
catalogRepository: catalog,
categoryRepository: categoryRepository,
watchHistoryRepository: InMemoryWatchHistoryRepository(),
watchHistoryRepository: history ?? InMemoryWatchHistoryRepository(),
favoriteRepository: InMemoryFavoriteRepository(),
playbackRepository: FakePlaybackRepository(),
commentRepository: FakeCommentRepository(),
+6 -21
View File
@@ -21,7 +21,7 @@ import '../support/widget_player.dart';
void main() {
testWidgets(
'should_render_home_feed_banners_when_discover_banners_are_empty',
'should_not_mount_carousel_on_discover_even_when_home_banners_exist',
(tester) async {
SharedPreferencesAsyncPlatform.instance =
InMemorySharedPreferencesAsync.empty();
@@ -43,26 +43,11 @@ void main() {
await tester.tap(find.byKey(const Key('tab-discover')));
await tester.pumpAndSettle();
expect(find.byType(CoverCarousel), findsOneWidget);
expect(find.byKey(const Key('carousel-dot-0')), findsOneWidget);
expect(find.byKey(const Key('carousel-dot-1')), findsOneWidget);
expect(
find.descendant(
of: find.byType(CoverCarousel),
matching: find.text('The Signal'),
),
findsOneWidget,
);
await tester.pump(const Duration(milliseconds: 4500));
await tester.pump(const Duration(milliseconds: 500));
expect(
find.descendant(
of: find.byType(CoverCarousel),
matching: find.text('City Lights & Velvet Nights'),
),
findsOneWidget,
);
expect(find.byType(CoverCarousel), findsNothing);
expect(find.byKey(const Key('discover-search')), findsOneWidget);
expect(find.byKey(const Key('home-all-dramas-grid')), findsOneWidget);
expect(find.byKey(const Key('discover-quick-filter')), findsOneWidget);
expect(find.text('The Signal'), findsWidgets);
await tester.pumpWidget(const SizedBox.shrink());
},
);