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? seekPreview; const PlayerPositionIndicator({ super.key, required this.player, this.seekPreview, }); @override State createState() => _PlayerPositionIndicatorState(); } class _PlayerPositionIndicatorState extends State { late Duration _position; late Duration _duration; StreamSubscription? _posSub; StreamSubscription? _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( valueListenable: seekPreview, builder: (context, preview, _) { final displayPos = preview ?? _position; return _buildText(displayPos, _duration); }, ); } return _buildText(_position, _duration); } }