import 'dart:io' show Platform; import 'package:flutter/material.dart'; import 'package:forui/forui.dart' as forui; import '../../../shared/widgets/all_versions_sheet.dart'; import '../../../shared/widgets/hover_scrollable_row.dart'; import '../../../shared/widgets/version_filter.dart'; import '../../../shared/widgets/version_grouping.dart'; import '../../../shared/widgets/version_source_card.dart'; import 'player_panel_shell.dart'; class VersionPanel extends StatefulWidget { final List entries; final VoidCallback onClose; const VersionPanel({super.key, required this.entries, required this.onClose}); @override State createState() => _VersionPanelState(); } class _VersionPanelState extends State { final ScrollController _scrollController = ScrollController(); String? _selectedBucket; @override void dispose() { _scrollController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final entries = widget.entries; final allGroups = groupVersionEntries(entries); final bucketsPresent = { for (final e in entries) versionBucketOf(e.resolutionLabel), }; final buckets = kVersionBucketOrder .where(bucketsPresent.contains) .toList(growable: false); final activeEntry = entries .where((entry) => entry.isCurrent || entry.selected) .firstOrNull; final defaultBucket = activeEntry == null ? (buckets.isNotEmpty ? buckets.first : null) : versionBucketOf(activeEntry.resolutionLabel); final selected = _selectedBucket != null && bucketsPresent.contains(_selectedBucket) ? _selectedBucket! : defaultBucket; if (selected != _selectedBucket) { WidgetsBinding.instance.addPostFrameCallback((_) { if (!mounted) return; setState(() => _selectedBucket = selected); }); } final filteredGroups = selected == null ? allGroups : allGroups .where( (group) => versionBucketOf(group.representative.resolutionLabel) == selected, ) .toList(growable: false); final inlineLimit = inlineVersionLimit(); final inlineGroups = filteredGroups .take(inlineLimit) .toList(growable: true); final activeGroupIndex = filteredGroups.indexWhere( (group) => group.hasActiveMember, ); if (activeGroupIndex >= inlineLimit && inlineGroups.isNotEmpty) { inlineGroups[inlineGroups.length - 1] = filteredGroups[activeGroupIndex]; } final hasMergedGroup = allGroups.any((group) => group.members.length > 1); final showAllVersionsButton = filteredGroups.length > inlineLimit || hasMergedGroup; final itemCount = inlineGroups.length; return Material( color: Colors.transparent, child: Container( decoration: BoxDecoration( color: kPanelBg, borderRadius: BorderRadius.circular(12), ), child: Column( mainAxisSize: MainAxisSize.min, children: [ _header( buckets, selected, allGroups: allGroups, sourceCount: entries.length, showAllVersionsButton: showAllVersionsButton, ), panelDivider(), Padding( padding: EdgeInsets.only( top: Platform.isAndroid ? 8 : 12, bottom: Platform.isAndroid ? 10 : 14, ), child: SizedBox( height: Platform.isAndroid ? 56 : 120, child: HoverScrollableRow( controller: _scrollController, builder: (_, controller) => ListView.builder( controller: controller, scrollDirection: Axis.horizontal, padding: EdgeInsets.symmetric( horizontal: Platform.isAndroid ? 10 : 16, ), itemCount: itemCount, itemBuilder: (_, i) => Padding( padding: EdgeInsets.only( right: i < itemCount - 1 ? (Platform.isAndroid ? 6 : 12) : 0, ), child: VersionSourceCard( data: inlineGroups[i].displayData, compact: Platform.isAndroid, onMergedServersTap: inlineGroups[i].members.length > 1 ? () => showAllVersionsSheet( context: context, groups: [inlineGroups[i]], title: '选择服务器', ) : null, ), ), ), ), ), ), ], ), ), ); } Widget _header( List buckets, String? selected, { required List allGroups, required int sourceCount, required bool showAllVersionsButton, }) { final isAndroid = Platform.isAndroid; return Padding( padding: isAndroid ? const EdgeInsets.fromLTRB(16, 10, 8, 8) : const EdgeInsets.fromLTRB(16, 12, 8, 10), child: Row( children: [ const Icon( Icons.video_file_outlined, color: Colors.white70, size: 18, ), const SizedBox(width: 8), Text( '切换版本', style: TextStyle( color: Colors.white, fontSize: isAndroid ? 14 : 15, fontWeight: FontWeight.w600, ), ), if (showAllVersionsButton) ...[ const SizedBox(width: 10), forui.FButton( variant: forui.FButtonVariant.secondary, size: forui.FButtonSizeVariant.xs, mainAxisSize: MainAxisSize.min, onPress: () => showAllVersionsSheet(context: context, groups: allGroups), child: Row( mainAxisSize: MainAxisSize.min, children: [ Text('全部 $sourceCount 个版本'), const SizedBox(width: 2), const Icon(Icons.chevron_right_rounded, size: 16), ], ), ), ], const SizedBox(width: 16), Expanded( child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: [ for (var i = 0; i < buckets.length; i++) ...[ if (i > 0) const SizedBox(width: 8), VersionFilterChip( label: buckets[i], selected: selected == buckets[i], showDot: true, onTap: () => setState(() => _selectedBucket = buckets[i]), ), ], ], ), ), ), const SizedBox(width: 8), IconButton( icon: const Icon(Icons.close, color: Colors.white54, size: 18), onPressed: widget.onClose, padding: EdgeInsets.zero, constraints: const BoxConstraints(minWidth: 32, minHeight: 32), ), ], ), ); } }