Files
sm-emby-share/lib/features/player/widgets/player_position_indicator.dart
2026-07-14 11:11:36 +08:00

100 lines
2.7 KiB
Dart

import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import '../../../core/infra/player_engine/player_engine.dart';
import '../../../shared/utils/format_utils.dart';
class PlayerPositionIndicator extends StatefulWidget {
final PlayerEngine player;
final ValueNotifier<Duration?>? seekPreview;
const PlayerPositionIndicator({
super.key,
required this.player,
this.seekPreview,
});
@override
State<PlayerPositionIndicator> createState() =>
_PlayerPositionIndicatorState();
}
class _PlayerPositionIndicatorState extends State<PlayerPositionIndicator> {
late Duration _position;
late Duration _duration;
StreamSubscription<Duration>? _posSub;
StreamSubscription<Duration>? _durSub;
@override
void initState() {
super.initState();
_position = widget.player.state.position;
_duration = widget.player.state.duration;
_posSub = widget.player.stream.position.listen((position) {
if (mounted) setState(() => _position = position);
});
_durSub = widget.player.stream.duration.listen((duration) {
if (mounted) setState(() => _duration = duration);
});
}
@override
void didUpdateWidget(covariant PlayerPositionIndicator oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.player != widget.player) {
unawaited(_posSub?.cancel());
unawaited(_durSub?.cancel());
_position = widget.player.state.position;
_duration = widget.player.state.duration;
_posSub = widget.player.stream.position.listen((position) {
if (mounted) setState(() => _position = position);
});
_durSub = widget.player.stream.duration.listen((duration) {
if (mounted) setState(() => _duration = duration);
});
}
}
@override
void dispose() {
unawaited(_posSub?.cancel());
unawaited(_durSub?.cancel());
super.dispose();
}
Widget _buildText(Duration position, Duration duration) {
return SizedBox(
width: 126,
child: Text(
'${formatDurationClock(position)} / ${formatDurationClock(duration)}',
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.white70,
fontSize: 13,
fontFeatures: [ui.FontFeature.tabularFigures()],
),
),
);
}
@override
Widget build(BuildContext context) {
final seekPreview = widget.seekPreview;
if (seekPreview != null) {
return ValueListenableBuilder<Duration?>(
valueListenable: seekPreview,
builder: (context, preview, _) {
final displayPos = preview ?? _position;
return _buildText(displayPos, _duration);
},
);
}
return _buildText(_position, _duration);
}
}