109 lines
3.2 KiB
Dart
109 lines
3.2 KiB
Dart
|
|
|
||
|
|
|
||
|
|
part of 'player_page.dart';
|
||
|
|
|
||
|
|
extension _PlayerPageVersionBar on _PlayerPageState {
|
||
|
|
|
||
|
|
|
||
|
|
Widget _buildVersionBarLayer(PlaybackSession session) {
|
||
|
|
return ValueListenableBuilder<bool>(
|
||
|
|
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<Offset>(
|
||
|
|
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<VersionSourceCardData> _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 = <VersionSourceCardData>[];
|
||
|
|
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;
|
||
|
|
}
|
||
|
|
}
|