part of 'player_page.dart'; extension _PlayerPageVersionBar on _PlayerPageState { Widget _buildVersionBarLayer(PlaybackSession session) { return ValueListenableBuilder( valueListenable: _versionBarOpen, builder: (context, open, _) { return Stack( fit: StackFit.expand, children: [ if (open) Positioned.fill( child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: () => _versionBarOpen.value = false, child: const SizedBox.expand(), ), ), Positioned( left: 16, right: 16, bottom: Platform.isAndroid ? 96 : 72, child: AnimatedSwitcher( duration: const Duration(milliseconds: 200), switchInCurve: Curves.easeOutCubic, switchOutCurve: Curves.easeInCubic, transitionBuilder: (child, anim) => FadeTransition( opacity: anim, child: SlideTransition( position: Tween( begin: const Offset(0, 0.15), end: Offset.zero, ).animate(anim), child: child, ), ), child: open ? _buildVersionBar(session) : const SizedBox.shrink(key: ValueKey('version-bar-empty')), ), ), ], ); }, ); } Widget _buildVersionBar(PlaybackSession session) { final entries = _buildVersionCardData(session); if (entries.isEmpty) { return const SizedBox.shrink(key: ValueKey('version-bar-empty')); } return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () {}, child: KeyedSubtree( key: const ValueKey('version-bar'), child: VersionPanel( entries: entries, onClose: () => _versionBarOpen.value = false, ), ), ); } List _buildVersionCardData(PlaybackSession session) { final ctx = session.context; final localSources = ctx.allMediaSources; final currentSourceId = ctx.mediaSourceId; final currentServerName = _lastRequest?.targetServer?.serverName ?? ref.read(activeSessionProvider)?.serverName ?? '本服务器'; final entries = []; for (final src in localSources) { final isCurrent = src.Id != null && src.Id == currentSourceId; final sourceId = src.Id; entries.add( VersionSourceCardData.fromLocalSource( src, serverName: currentServerName, serverKey: ctx.serverId, isCurrent: isCurrent, tooltip: isCurrent ? null : '以该版本播放', onTap: (isCurrent || sourceId == null) ? null : () => _switchVersion(sourceId), ), ); } for (final card in _crossServerCards) { entries.add( VersionSourceCardData.fromCrossServer( card, onTap: () => _switchToCrossServer(card), ), ); } return entries; } }