From 9aa797504b1b7f0e8666dedfbe5cd479c8c9e1f1 Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Thu, 22 Oct 2026 22:48:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=BB=9F=E4=B8=80=E9=A6=96=E9=A1=B5?= =?UTF-8?q?=E4=B8=8E=E6=8E=A8=E8=8D=90=E6=90=9C=E7=B4=A2=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/plans/PROGRESS.md | 6 + lib/l10n/app_en.arb | 1 + lib/l10n/app_id.arb | 1 + lib/l10n/app_localizations.dart | 6 + lib/l10n/app_localizations_en.dart | 3 + lib/l10n/app_localizations_id.dart | 3 + lib/l10n/app_localizations_zh.dart | 3 + lib/l10n/app_zh.arb | 1 + lib/pages/discover/discover_logic.dart | 14 - lib/pages/discover/discover_view.dart | 22 -- lib/pages/discover/search_page.dart | 256 ++++++++++++++++++ .../widgets/discover_search_results.dart | 11 +- lib/pages/home/home_logic.dart | 8 +- lib/pages/home/home_view.dart | 8 +- lib/pages/player/player_logic.dart | 21 +- test/discover/discover_logic_test.dart | 38 --- test/home/home_logic_test.dart | 9 +- test/home/k28_a_navigation_test.dart | 21 +- 18 files changed, 325 insertions(+), 107 deletions(-) create mode 100644 lib/pages/discover/search_page.dart diff --git a/docs/plans/PROGRESS.md b/docs/plans/PROGRESS.md index 39d0c4d..4ca1922 100644 --- a/docs/plans/PROGRESS.md +++ b/docs/plans/PROGRESS.md @@ -1304,3 +1304,9 @@ $ flutter test - 移除视频表面点击识别器,双击后增加 300ms 暂停防护;飘心收敛为 3~4 颗、20~26、±18、90~120dp、600~700ms。 - 双击窗口延长至 320ms,单击 Timer 使用点击序号校验,双击后保留 350ms 播放切换保护。 - `double_tap_like_test.dart` 覆盖 120/250/300ms 双击与 500ms 单击恢复:2 项测试全部通过。 + +## 2026-10-22 统一二级搜索页 + +- 首页与推荐/剧单/全部短剧搜索入口统一 push SearchPage,不再切换推荐 tab 聚焦。 +- 搜索页提供返回、自动聚焦输入框与取消,复用 `/app/search` 逻辑和首页三列卡片。 +- 清理旧的 searchRequest/focusSearch/syncSearch 参数;`flutter analyze`:No issues found。 diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 58f9e34..4549c2d 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -16,6 +16,7 @@ "currentSeries": "Current series", "discoverTab": "Theater", "searchDiscover": "Search dramas, actors, genres...", + "searchCancel": "Cancel", "featured": "Featured", "recommended": "Recommended", "fruitShortsBrand": "FruitShorts", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index a05c235..f0ae1ba 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -16,6 +16,7 @@ "currentSeries": "Serial saat ini", "discoverTab": "Teater", "searchDiscover": "Cari drama, aktor, genre...", + "searchCancel": "Batal", "featured": "Unggulan", "recommended": "Rekomendasi", "fruitShortsBrand": "FruitShorts", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index e6d0fa0..42223d4 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -196,6 +196,12 @@ abstract class AppLocalizations { /// **'Search dramas, actors, genres...'** String get searchDiscover; + /// No description provided for @searchCancel. + /// + /// In en, this message translates to: + /// **'Cancel'** + String get searchCancel; + /// No description provided for @featured. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index a8d5b9e..96f45c6 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -57,6 +57,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get searchDiscover => 'Search dramas, actors, genres...'; + @override + String get searchCancel => 'Cancel'; + @override String get featured => 'Featured'; diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index 8d05415..9a3aeb7 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -57,6 +57,9 @@ class AppLocalizationsId extends AppLocalizations { @override String get searchDiscover => 'Cari drama, aktor, genre...'; + @override + String get searchCancel => 'Batal'; + @override String get featured => 'Unggulan'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index c4e1ab3..62f7489 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -56,6 +56,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get searchDiscover => '搜索短剧、演员、类型…'; + @override + String get searchCancel => '取消'; + @override String get featured => '精选'; diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 4954462..9af6280 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -16,6 +16,7 @@ "currentSeries": "当前短剧", "discoverTab": "剧场", "searchDiscover": "搜索短剧、演员、类型…", + "searchCancel": "取消", "featured": "精选", "recommended": "推荐", "fruitShortsBrand": "水果短剧", diff --git a/lib/pages/discover/discover_logic.dart b/lib/pages/discover/discover_logic.dart index d500ed7..7a96911 100644 --- a/lib/pages/discover/discover_logic.dart +++ b/lib/pages/discover/discover_logic.dart @@ -118,20 +118,6 @@ class DiscoverLogic extends ChangeNotifier { searchResults = const []; } - /// 从播放器发起的搜索请求只在剧场页可见时抢焦点。 - void syncSearch({ - required bool active, - required int request, - required int previousRequest, - required bool Function() isActive, - }) { - if (!active || request == previousRequest) return; - WidgetsBinding.instance.addPostFrameCallback((_) { - if (isActive()) searchFocus.requestFocus(); - }); - WidgetsBinding.instance.scheduleFrame(); - } - void act( BuildContext context, DiscoverPageAction action, { diff --git a/lib/pages/discover/discover_view.dart b/lib/pages/discover/discover_view.dart index f82d713..6fe6d64 100644 --- a/lib/pages/discover/discover_view.dart +++ b/lib/pages/discover/discover_view.dart @@ -56,14 +56,10 @@ class DiscoverPage extends StatefulWidget { const DiscoverPage({ super.key, this.active = true, - this.searchRequest = 0, - this.focusSearch = false, this.callbacks = const DiscoverPageCallbacks(), }); final bool active; - final int searchRequest; - final bool focusSearch; final DiscoverPageCallbacks callbacks; @override @@ -89,7 +85,6 @@ class _DiscoverPageState extends State { logic.scroll.addListener(_onScroll); theater.addListener(_onTheaterChanged); unawaited(theater.select(null)); - if (widget.focusSearch) _focusOnFirstFrame(); } void _onScroll() { @@ -106,23 +101,6 @@ class _DiscoverPageState extends State { if (mounted) setState(() {}); } - void _focusOnFirstFrame() { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted && widget.active) logic.openSearch(); - }); - } - - @override - void didUpdateWidget(covariant DiscoverPage oldWidget) { - super.didUpdateWidget(oldWidget); - logic.syncSearch( - active: widget.active, - request: widget.searchRequest, - previousRequest: oldWidget.searchRequest, - isActive: () => mounted && widget.active, - ); - } - @override void dispose() { logic.scroll.removeListener(_onScroll); diff --git a/lib/pages/discover/search_page.dart b/lib/pages/discover/search_page.dart new file mode 100644 index 0000000..bc4ac1c --- /dev/null +++ b/lib/pages/discover/search_page.dart @@ -0,0 +1,256 @@ +// Author: fruit.com.cn + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; + +import '../../core/data/repositories/search_repository.dart'; +import '../../core/formatting/error_text.dart'; +import '../../core/theme/app_colors.dart'; +import '../../core/theme/app_dimens.dart'; +import '../../core/theme/app_theme.dart'; +import '../../core/widgets/empty_state.dart'; +import '../../l10n/app_localizations.dart'; +import '../home/widgets/home_feed_card.dart'; +import '../player/open_player.dart'; +import 'discover_binding.dart'; +import 'discover_logic.dart'; + +class SearchPage extends StatefulWidget { + const SearchPage({super.key}); + + @override + State createState() => _SearchPageState(); +} + +class _SearchPageState extends State { + late final DiscoverLogic _logic; + + @override + void initState() { + super.initState(); + _logic = DiscoverBinding().create(search: context.read()); + _logic.addListener(_rebuild); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) _logic.searchFocus.requestFocus(); + }); + } + + void _rebuild() { + if (mounted) setState(() {}); + } + + @override + void dispose() { + _logic + ..removeListener(_rebuild) + ..dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context)!; + return AnnotatedRegion( + value: SystemUiOverlayStyle.light, + child: Theme( + data: AppTheme.dark(), + child: Scaffold( + key: const Key('search-page'), + backgroundColor: AppColors.appBackground, + body: SafeArea( + child: Column( + children: [ + _header(l10n), + Expanded(child: _body(l10n)), + ], + ), + ), + ), + ), + ); + } + + Widget _header(AppLocalizations l10n) { + return Padding( + padding: const EdgeInsets.fromLTRB(8, 6, 14, 8), + child: SizedBox( + height: AppDimens.searchHeight, + child: Row( + children: [ + SizedBox.square( + dimension: AppDimens.buttonMedium, + child: IconButton( + key: const Key('search-back'), + padding: EdgeInsets.zero, + onPressed: () => Navigator.of(context).maybePop(), + icon: const Icon( + Icons.arrow_back_ios_new_rounded, + color: AppColors.textPrimary, + size: 22, + ), + ), + ), + const SizedBox(width: AppDimens.spaceSm), + Expanded(child: _field(l10n)), + const SizedBox(width: AppDimens.spaceSm), + TextButton( + key: const Key('search-cancel'), + onPressed: () => Navigator.of(context).maybePop(), + child: Text(l10n.searchCancel), + ), + ], + ), + ), + ); + } + + Widget _field(AppLocalizations l10n) { + final hasText = _logic.search.text.isNotEmpty; + return Container( + key: const Key('search-field'), + height: AppDimens.searchHeight, + padding: const EdgeInsets.symmetric(horizontal: AppDimens.spaceMd), + decoration: BoxDecoration( + color: AppColors.contentSurface, + borderRadius: BorderRadius.circular(AppDimens.searchRadius), + ), + child: Row( + children: [ + const Icon( + Icons.search_rounded, + color: AppColors.textSecondary, + size: 18, + ), + const SizedBox(width: AppDimens.spaceSm), + Expanded( + child: TextField( + key: const Key('discover-search'), + controller: _logic.search, + focusNode: _logic.searchFocus, + autofocus: true, + onChanged: (_) => _logic.onSearchChanged(), + textInputAction: TextInputAction.search, + style: const TextStyle( + color: AppColors.textPrimary, + fontSize: 14, + ), + decoration: InputDecoration( + isDense: true, + hintText: l10n.searchDiscover, + hintStyle: const TextStyle( + color: AppColors.textSecondary, + fontSize: 13, + ), + border: InputBorder.none, + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + contentPadding: EdgeInsets.zero, + ), + ), + ), + if (_logic.searchLoading) + const SizedBox.square( + dimension: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + color: AppColors.textPrimary, + ), + ) + else if (hasText) + IconButton( + key: const Key('search-clear'), + padding: EdgeInsets.zero, + constraints: const BoxConstraints.tightFor(width: 24, height: 24), + onPressed: () { + _logic.search.clear(); + _logic.onSearchChanged(); + _logic.searchFocus.requestFocus(); + }, + icon: const Icon( + Icons.close_rounded, + color: AppColors.textSecondary, + size: 16, + ), + ), + ], + ), + ); + } + + Widget _body(AppLocalizations l10n) { + if (!_logic.hasSearchQuery) { + return EmptyState( + key: const Key('search-idle'), + icon: Icons.search_rounded, + title: l10n.searchDiscover, + ); + } + if (_logic.searchLoading) { + return const Center( + child: SizedBox.square( + key: Key('search-loading'), + dimension: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + color: AppColors.textPrimary, + ), + ), + ); + } + if (_logic.searchError != null) { + return EmptyState( + key: const Key('search-error'), + icon: Icons.cloud_off_rounded, + title: errorText(l10n, _logic.searchError, l10n.searchFailed), + actionLabel: l10n.retry, + onAction: _logic.retrySearch, + ); + } + if (_logic.searchResults.isEmpty) { + return EmptyState( + key: const Key('search-empty'), + icon: Icons.search_off_rounded, + title: l10n.noSearchResults, + subtitle: l10n.emptyHint, + ); + } + return LayoutBuilder( + builder: (context, constraints) { + final width = + (constraints.maxWidth - + AppDimens.feedPageHorizontal * 2 - + AppDimens.homeGridGap * 2) / + 3; + final itemExtent = + width / AppDimens.cardCoverAspectRatio + + AppDimens.homeCardBodyExtent; + return GridView.builder( + key: const Key('search-results-grid'), + padding: const EdgeInsets.fromLTRB( + AppDimens.feedPageHorizontal, + AppDimens.spaceMd, + AppDimens.feedPageHorizontal, + AppDimens.spaceXl, + ), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + crossAxisSpacing: AppDimens.homeGridGap, + mainAxisSpacing: AppDimens.homeGridRowGap, + mainAxisExtent: itemExtent, + ), + itemCount: _logic.searchResults.length, + itemBuilder: (context, index) { + final drama = _logic.searchResults[index]; + return HomeFeedCard( + drama: drama, + onTap: () => unawaited(playDramaFromCatalog(context, drama)), + ); + }, + ); + }, + ); + } +} diff --git a/lib/pages/discover/widgets/discover_search_results.dart b/lib/pages/discover/widgets/discover_search_results.dart index dfaa0d5..51b9281 100644 --- a/lib/pages/discover/widgets/discover_search_results.dart +++ b/lib/pages/discover/widgets/discover_search_results.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import '../../../core/data/models/drama.dart'; import '../../../core/formatting/error_text.dart'; +import '../../../core/theme/app_colors.dart'; import '../../../core/widgets/empty_state.dart'; import '../../../l10n/app_localizations.dart'; import 'drama_grid.dart'; @@ -38,7 +39,15 @@ class DiscoverSearchResults extends StatelessWidget { if (loading) { return const Padding( padding: EdgeInsets.symmetric(vertical: 80), - child: Center(child: CircularProgressIndicator(strokeWidth: 2)), + child: Center( + child: SizedBox.square( + dimension: 20, + child: CircularProgressIndicator( + strokeWidth: 2, + color: AppColors.textPrimary, + ), + ), + ), ); } final message = error; diff --git a/lib/pages/home/home_logic.dart b/lib/pages/home/home_logic.dart index d82c60c..7e5dc6d 100644 --- a/lib/pages/home/home_logic.dart +++ b/lib/pages/home/home_logic.dart @@ -17,7 +17,6 @@ class HomeLogic extends ChangeNotifier { final scaffoldKey = GlobalKey(); Drama currentDrama; int tab = 0; - int searchRequest = 0; int resumeRequest = 0; int backRequest = 0; int followingHistoryRequest = 0; @@ -31,10 +30,9 @@ class HomeLogic extends ChangeNotifier { if (!_disposed) notifyListeners(); } - void select(int nextTab, {bool search = false, bool resume = false}) { + void select(int nextTab, {bool resume = false}) { FocusManager.instance.primaryFocus?.unfocus(); tab = nextTab; - if (search) searchRequest++; if (resume) resumeRequest++; notify(); } @@ -81,7 +79,7 @@ class HomeLogic extends ChangeNotifier { case DiscoverPageAction.home: select(0); case DiscoverPageAction.recommend: - select(1, search: true); + select(1); case DiscoverPageAction.following: select(2); case DiscoverPageAction.mine: @@ -112,7 +110,7 @@ class HomeLogic extends ChangeNotifier { case MyPageAction.home: select(0); case MyPageAction.recommend: - select(1, search: true); + select(1); case MyPageAction.following: select(2); case MyPageAction.mine: diff --git a/lib/pages/home/home_view.dart b/lib/pages/home/home_view.dart index cb1676c..bdf3401 100644 --- a/lib/pages/home/home_view.dart +++ b/lib/pages/home/home_view.dart @@ -22,7 +22,7 @@ import '../../core/widgets/app_root_tab.dart'; import '../../core/widgets/catalog_endpoint_editor.dart'; import '../../core/widgets/empty_state.dart'; import '../../l10n/app_localizations.dart'; -import '../discover/discover_view.dart'; +import '../discover/search_page.dart'; import '../following/following_page.dart'; import '../filter/filter_page.dart'; import '../mine/mine_types.dart'; @@ -292,9 +292,9 @@ class _HomeTabsState extends State<_HomeTabs> { } void _openSearch(BuildContext context) { - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const DiscoverPage(focusSearch: true)), - ); + Navigator.of( + context, + ).push(MaterialPageRoute(builder: (_) => const SearchPage())); } void _pushPage(BuildContext context, Widget page) { diff --git a/lib/pages/player/player_logic.dart b/lib/pages/player/player_logic.dart index 8060bd8..8cc6877 100644 --- a/lib/pages/player/player_logic.dart +++ b/lib/pages/player/player_logic.dart @@ -26,8 +26,7 @@ import '../../core/formatting/localized_content.dart'; import '../../core/theme/app_colors.dart'; import '../../core/navigation/fade_page_route.dart'; import '../../l10n/app_localizations.dart'; -import '../discover/discover_types.dart'; -import '../discover/discover_view.dart'; +import '../discover/search_page.dart'; import '../drama_detail/drama_detail_view.dart'; import '../mine/mine_types.dart'; import '../mine/mine_view.dart'; @@ -1127,13 +1126,10 @@ class PlayerLogic extends ChangeNotifier { onSearch?.call(); return; } - await showDiscover(context, focusSearch: true); + await showDiscover(context); } - Future showDiscover( - BuildContext context, { - bool focusSearch = false, - }) async { + Future showDiscover(BuildContext context) async { if (embedded) { onDiscover?.call(); return; @@ -1142,13 +1138,10 @@ class PlayerLogic extends ChangeNotifier { await session.playbackSettled; await saveCurrentProgress(); if (!context.mounted) return; - final action = await Navigator.of(context).push( - MaterialPageRoute(builder: (_) => DiscoverPage(focusSearch: focusSearch)), - ); - if (!context.mounted) return; - if (action == DiscoverPageAction.mine) { - await showMy(context); - } else { + await Navigator.of( + context, + ).push(MaterialPageRoute(builder: (_) => const SearchPage())); + if (context.mounted) { session.setRouteVisible(true); } } diff --git a/test/discover/discover_logic_test.dart b/test/discover/discover_logic_test.dart index 80bcfe8..f73a5e5 100644 --- a/test/discover/discover_logic_test.dart +++ b/test/discover/discover_logic_test.dart @@ -23,44 +23,6 @@ void main() { logic.dispose(); }); - testWidgets('should_focus_search_only_for_new_request_on_active_page', ( - tester, - ) async { - final logic = DiscoverLogic(); - await tester.pumpWidget( - MaterialApp( - home: Scaffold( - body: TextField( - controller: logic.search, - focusNode: logic.searchFocus, - ), - ), - ), - ); - - logic.syncSearch( - active: false, - request: 1, - previousRequest: 0, - isActive: () => false, - ); - await tester.pump(); - expect(logic.searchFocus.hasFocus, isFalse); - - logic.syncSearch( - active: true, - request: 2, - previousRequest: 1, - isActive: () => true, - ); - await tester.pump(); - await tester.pump(); - expect(logic.searchFocus.hasFocus, isTrue); - - await tester.pumpWidget(const SizedBox.shrink()); - logic.dispose(); - }); - testWidgets('should_forward_action_when_embedded_or_pop_when_standalone', ( tester, ) async { diff --git a/test/home/home_logic_test.dart b/test/home/home_logic_test.dart index 0b67f63..44b4dce 100644 --- a/test/home/home_logic_test.dart +++ b/test/home/home_logic_test.dart @@ -42,23 +42,17 @@ void main() { logic.select(1); expect(logic.tab, 1); - expect(logic.searchRequest, 0); expect(logic.resumeRequest, 0); expect(logic.backRequest, 0); expect(notifications, 1); logic.dispose(); }); - test('should_raise_only_its_own_counter_when_each_select_flag_is_used', () { + test('should_raise_only_resume_counter_when_select_flag_is_used', () { final logic = HomeLogic(dramas); - logic.select(1, search: true); - expect(logic.searchRequest, 1); - expect(logic.resumeRequest, 0); - logic.select(0, resume: true); expect(logic.resumeRequest, 1); - expect(logic.searchRequest, 1); logic.dispose(); }); @@ -161,7 +155,6 @@ void main() { logic.myAction(MyPageAction.recommend); expect(logic.tab, 1); - expect(logic.searchRequest, 1); logic.myAction(MyPageAction.following); expect(logic.tab, 2); diff --git a/test/home/k28_a_navigation_test.dart b/test/home/k28_a_navigation_test.dart index cec7ade..5e5eda7 100644 --- a/test/home/k28_a_navigation_test.dart +++ b/test/home/k28_a_navigation_test.dart @@ -118,6 +118,10 @@ void main() { await _pumpApp(tester); await tester.tap(find.byKey(const Key('home-search-entry'))); await tester.pumpAndSettle(); + expect(find.byKey(const Key('search-page')), findsOneWidget); + expect(find.byKey(const Key('search-back')), findsOneWidget); + expect(find.byKey(const Key('search-cancel')), findsOneWidget); + expect(find.byType(AppBottomNav), findsNothing); expect( tester .widget(find.byKey(const Key('discover-search'))) @@ -125,7 +129,7 @@ void main() { ?.hasFocus, isTrue, ); - await tester.binding.handlePopRoute(); + await tester.tap(find.byKey(const Key('search-cancel'))); await tester.pumpAndSettle(); await tester.tap(find.byKey(const Key('tab-recommend'))); @@ -133,6 +137,20 @@ void main() { await tester.tap(find.byKey(const Key('recommend-search'))); await tester.pumpAndSettle(); expect(find.byKey(const Key('discover-search')), findsOneWidget); + await tester.enterText(find.byKey(const Key('discover-search')), 'signal'); + await tester.pump(const Duration(milliseconds: 400)); + await tester.pumpAndSettle(); + expect(find.byKey(const Key('search-results-grid')), findsOneWidget); + final results = tester.widget( + find.byKey(const Key('search-results-grid')), + ); + final delegate = + results.gridDelegate as SliverGridDelegateWithFixedCrossAxisCount; + expect(delegate.crossAxisCount, 3); + await tester.tap(find.byKey(const Key('search-back'))); + await tester.pumpAndSettle(); + expect(find.byKey(const Key('recommend-page')), findsOneWidget); + expect(find.byKey(const Key('recommend-tab-recommend')), findsOneWidget); }); testWidgets('should_open_player_from_home_grid_card', (tester) async { @@ -188,6 +206,7 @@ Future _pumpApp( playerFactory: createWidgetPlayer, catalogRepository: catalog, categoryRepository: FakeCategoryRepository(catalog: catalog), + searchRepository: catalog, watchHistoryRepository: InMemoryWatchHistoryRepository(), favoriteRepository: InMemoryFavoriteRepository(cards: catalog.dramas), playbackRepository: FakePlaybackRepository(),