// Author: fruit.com.cn import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import '../../core/data/services/favorite_controller.dart'; import '../../core/formatting/error_text.dart'; import '../../l10n/app_localizations.dart'; import '../player/open_player.dart'; import 'following_grid_card.dart'; import 'following_paged_grid.dart'; /// 追剧编辑中的选中状态。 class FollowingFavoriteSelection { const FollowingFavoriteSelection({ required this.editing, required this.selected, required this.onToggle, }); final ValueListenable editing; final ValueListenable> selected; final ValueChanged onToggle; } /// 追剧列表。编辑时点卡片只切换选中,不进入播放。 class FollowingFavoritesGrid extends StatelessWidget { const FollowingFavoritesGrid({ super.key, required this.controller, required this.selection, required this.scrollController, required this.onGoHome, }); final FavoriteController controller; final FollowingFavoriteSelection selection; final ScrollController scrollController; final VoidCallback onGoHome; @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: selection.editing, builder: (context, isEditing, _) { return ValueListenableBuilder>( valueListenable: selection.selected, builder: (context, picked, _) { return ListenableBuilder( listenable: controller, builder: (context, _) => _grid(context, isEditing, picked), ); }, ); }, ); } Widget _grid(BuildContext context, bool isEditing, Set picked) { final l10n = AppLocalizations.of(context)!; final extra = isEditing ? 0 : 1; return FollowingPagedGrid( key: const Key('following-favorites-grid'), scrollController: scrollController, loading: controller.loading, loadingMore: controller.loadingMore, hasMore: controller.hasMore, hasItems: controller.cards.isNotEmpty, showInitialLoading: false, errorMessage: controller.error == null ? null : errorText(l10n, controller.error, l10n.favoritesError), loadMoreError: controller.loadMoreError == null ? null : errorText(l10n, controller.loadMoreError, l10n.favoritesError), onRefresh: controller.load, onLoadMore: controller.loadMore, itemCount: controller.cards.length + extra, empty: const SizedBox.shrink(), itemBuilder: (context, index) => _item(context, index, isEditing, picked), ); } Widget _item( BuildContext context, int index, bool isEditing, Set picked, ) { if (index == controller.cards.length) { return FollowingAddCard(onTap: onGoHome); } final l10n = AppLocalizations.of(context)!; final drama = controller.cards[index]; final episode = drama.episodes.isEmpty ? 1 : drama.episodes.first.number; return FollowingGridCard( dramaId: drama.id, title: drama.title, posterUrl: drama.posterUrl, viewCount: drama.viewCount, editing: isEditing, selected: isEditing ? picked.contains(drama.id) : null, subtitle: l10n.favoriteEpisodeProgress(episode, drama.episodeCount), onTap: () => _openOrToggle(context, drama.id, isEditing), ); } void _openOrToggle(BuildContext context, String dramaId, bool isEditing) { if (isEditing) { selection.onToggle(dramaId); return; } final drama = controller.cards.firstWhere((item) => item.id == dramaId); unawaited(playDramaFromCatalog(context, drama)); } }