@@ -301,6 +301,7 @@
|
||||
"rotationLockButton": "Drehsperre",
|
||||
"timelineSlider": "Video-Zeitleiste",
|
||||
"volumeSlider": "Lautstärkepegel",
|
||||
"endsAt": "Endet um ${time}",
|
||||
"backButton": "Zurück",
|
||||
"pipFailed": "Bild-in-Bild konnte nicht gestartet werden",
|
||||
"pipErrors": {
|
||||
|
||||
@@ -300,6 +300,7 @@
|
||||
"rotationLockButton": "Rotation lock",
|
||||
"timelineSlider": "Video timeline",
|
||||
"volumeSlider": "Volume level",
|
||||
"endsAt": "Ends at ${time}",
|
||||
"backButton": "Back",
|
||||
"pipFailed": "Picture-in-picture failed to start",
|
||||
"pipErrors": {
|
||||
|
||||
@@ -300,6 +300,7 @@
|
||||
"rotationLockButton": "Verrouillage de rotation",
|
||||
"timelineSlider": "Timeline vidéo",
|
||||
"volumeSlider": "Niveau sonore",
|
||||
"endsAt": "Fin à ${time}",
|
||||
"backButton": "Retour",
|
||||
"pipFailed": "Échec du démarrage du mode image dans l'image",
|
||||
"pipErrors": {
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
"rotationLockButton": "Blocco rotazione",
|
||||
"timelineSlider": "Timeline video",
|
||||
"volumeSlider": "Livello volume",
|
||||
"endsAt": "Finisce alle ${time}",
|
||||
"backButton": "Indietro",
|
||||
"pipFailed": "Impossibile avviare la modalità Picture-in-Picture",
|
||||
"pipErrors": {
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
"rotationLockButton": "회전 잠금",
|
||||
"timelineSlider": "타임라인",
|
||||
"volumeSlider": "볼륨 조절",
|
||||
"endsAt": "${time}에 종료",
|
||||
"backButton": "뒤로 가기",
|
||||
"pipFailed": "화면 속 화면 모드를 시작할 수 없습니다",
|
||||
"pipErrors": {
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
"rotationLockButton": "Rotatievergrendeling",
|
||||
"timelineSlider": "Videotijdlijn",
|
||||
"volumeSlider": "Volumeniveau",
|
||||
"endsAt": "Eindigt om ${time}",
|
||||
"backButton": "Terug",
|
||||
"pipFailed": "Beeld-in-beeld kon niet worden gestart",
|
||||
"pipErrors": {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
/// To regenerate, run: `dart run slang`
|
||||
///
|
||||
/// Locales: 8
|
||||
/// Strings: 4540 (567 per locale)
|
||||
/// Strings: 4548 (568 per locale)
|
||||
///
|
||||
/// Built on 2026-01-29 at 06:02 UTC
|
||||
/// Built on 2026-02-01 at 12:48 UTC
|
||||
|
||||
// coverage:ignore-file
|
||||
// ignore_for_file: type=lint, unused_import
|
||||
|
||||
@@ -452,6 +452,7 @@ class _TranslationsVideoControlsDe implements TranslationsVideoControlsEn {
|
||||
@override String get rotationLockButton => 'Drehsperre';
|
||||
@override String get timelineSlider => 'Video-Zeitleiste';
|
||||
@override String get volumeSlider => 'Lautstärkepegel';
|
||||
@override String endsAt({required Object time}) => 'Endet um ${time}';
|
||||
@override String get backButton => 'Zurück';
|
||||
@override String get pipFailed => 'Bild-in-Bild konnte nicht gestartet werden';
|
||||
@override late final _TranslationsVideoControlsPipErrorsDe pipErrors = _TranslationsVideoControlsPipErrorsDe._(_root);
|
||||
@@ -1258,6 +1259,7 @@ extension on TranslationsDe {
|
||||
'videoControls.rotationLockButton' => 'Drehsperre',
|
||||
'videoControls.timelineSlider' => 'Video-Zeitleiste',
|
||||
'videoControls.volumeSlider' => 'Lautstärkepegel',
|
||||
'videoControls.endsAt' => ({required Object time}) => 'Endet um ${time}',
|
||||
'videoControls.backButton' => 'Zurück',
|
||||
'videoControls.pipFailed' => 'Bild-in-Bild konnte nicht gestartet werden',
|
||||
'videoControls.pipErrors.androidVersion' => 'Erfordert Android 8.0 oder neuer',
|
||||
@@ -1496,9 +1498,9 @@ extension on TranslationsDe {
|
||||
'collections.addedToCollection' => 'Zur Sammlung hinzugefügt',
|
||||
'collections.errorAddingToCollection' => 'Fehler beim Hinzufügen zur Sammlung',
|
||||
'collections.created' => 'Sammlung erstellt',
|
||||
'collections.removeFromCollection' => 'Aus Sammlung entfernen',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'collections.removeFromCollection' => 'Aus Sammlung entfernen',
|
||||
'collections.removeFromCollectionConfirm' => ({required Object title}) => '"${title}" aus dieser Sammlung entfernen?',
|
||||
'collections.removedFromCollection' => 'Aus Sammlung entfernt',
|
||||
'collections.removeFromCollectionFailed' => 'Entfernen aus Sammlung fehlgeschlagen',
|
||||
|
||||
@@ -964,6 +964,9 @@ class TranslationsVideoControlsEn {
|
||||
/// en: 'Volume level'
|
||||
String get volumeSlider => 'Volume level';
|
||||
|
||||
/// en: 'Ends at ${time}'
|
||||
String endsAt({required Object time}) => 'Ends at ${time}';
|
||||
|
||||
/// en: 'Back'
|
||||
String get backButton => 'Back';
|
||||
|
||||
@@ -2393,6 +2396,7 @@ extension on Translations {
|
||||
'videoControls.rotationLockButton' => 'Rotation lock',
|
||||
'videoControls.timelineSlider' => 'Video timeline',
|
||||
'videoControls.volumeSlider' => 'Volume level',
|
||||
'videoControls.endsAt' => ({required Object time}) => 'Ends at ${time}',
|
||||
'videoControls.backButton' => 'Back',
|
||||
'videoControls.pipFailed' => 'Picture-in-picture failed to start',
|
||||
'videoControls.pipErrors.androidVersion' => 'Requires Android 8.0 or newer',
|
||||
@@ -2632,9 +2636,9 @@ extension on Translations {
|
||||
'watchTogether.hostOnly' => 'Host Only',
|
||||
'watchTogether.anyone' => 'Anyone',
|
||||
'watchTogether.hostingSession' => 'Hosting Session',
|
||||
'watchTogether.inSession' => 'In Session',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'watchTogether.inSession' => 'In Session',
|
||||
'watchTogether.sessionCode' => 'Session Code',
|
||||
'watchTogether.hostControlsPlayback' => 'Host controls playback',
|
||||
'watchTogether.anyoneCanControl' => 'Anyone can control playback',
|
||||
|
||||
@@ -451,6 +451,7 @@ class _TranslationsVideoControlsFr implements TranslationsVideoControlsEn {
|
||||
@override String get rotationLockButton => 'Verrouillage de rotation';
|
||||
@override String get timelineSlider => 'Timeline vidéo';
|
||||
@override String get volumeSlider => 'Niveau sonore';
|
||||
@override String endsAt({required Object time}) => 'Fin à ${time}';
|
||||
@override String get backButton => 'Retour';
|
||||
@override String get pipFailed => 'Échec du démarrage du mode image dans l\'image';
|
||||
@override late final _TranslationsVideoControlsPipErrorsFr pipErrors = _TranslationsVideoControlsPipErrorsFr._(_root);
|
||||
@@ -1255,6 +1256,7 @@ extension on TranslationsFr {
|
||||
'videoControls.rotationLockButton' => 'Verrouillage de rotation',
|
||||
'videoControls.timelineSlider' => 'Timeline vidéo',
|
||||
'videoControls.volumeSlider' => 'Niveau sonore',
|
||||
'videoControls.endsAt' => ({required Object time}) => 'Fin à ${time}',
|
||||
'videoControls.backButton' => 'Retour',
|
||||
'videoControls.pipFailed' => 'Échec du démarrage du mode image dans l\'image',
|
||||
'videoControls.pipErrors.androidVersion' => 'Nécessite Android 8.0 ou plus récent',
|
||||
@@ -1494,9 +1496,9 @@ extension on TranslationsFr {
|
||||
'watchTogether.hostOnly' => 'Hôte uniquement',
|
||||
'watchTogether.anyone' => 'N\'importe qui',
|
||||
'watchTogether.hostingSession' => 'Session d\'hébergement',
|
||||
'watchTogether.inSession' => 'En session',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'watchTogether.inSession' => 'En session',
|
||||
'watchTogether.sessionCode' => 'Code de session',
|
||||
'watchTogether.hostControlsPlayback' => 'L\'hôte contrôle la lecture',
|
||||
'watchTogether.anyoneCanControl' => 'Tout le monde peut contrôler la lecture',
|
||||
|
||||
@@ -452,6 +452,7 @@ class _TranslationsVideoControlsIt implements TranslationsVideoControlsEn {
|
||||
@override String get rotationLockButton => 'Blocco rotazione';
|
||||
@override String get timelineSlider => 'Timeline video';
|
||||
@override String get volumeSlider => 'Livello volume';
|
||||
@override String endsAt({required Object time}) => 'Finisce alle ${time}';
|
||||
@override String get backButton => 'Indietro';
|
||||
@override String get pipFailed => 'Impossibile avviare la modalità Picture-in-Picture';
|
||||
@override late final _TranslationsVideoControlsPipErrorsIt pipErrors = _TranslationsVideoControlsPipErrorsIt._(_root);
|
||||
@@ -1258,6 +1259,7 @@ extension on TranslationsIt {
|
||||
'videoControls.rotationLockButton' => 'Blocco rotazione',
|
||||
'videoControls.timelineSlider' => 'Timeline video',
|
||||
'videoControls.volumeSlider' => 'Livello volume',
|
||||
'videoControls.endsAt' => ({required Object time}) => 'Finisce alle ${time}',
|
||||
'videoControls.backButton' => 'Indietro',
|
||||
'videoControls.pipFailed' => 'Impossibile avviare la modalità Picture-in-Picture',
|
||||
'videoControls.pipErrors.androidVersion' => 'Richiede Android 8.0 o versioni successive',
|
||||
@@ -1496,9 +1498,9 @@ extension on TranslationsIt {
|
||||
'collections.addedToCollection' => 'Aggiunto alla raccolta',
|
||||
'collections.errorAddingToCollection' => 'Errore nell\'aggiunta alla raccolta',
|
||||
'collections.created' => 'Raccolta creata',
|
||||
'collections.removeFromCollection' => 'Rimuovi dalla raccolta',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'collections.removeFromCollection' => 'Rimuovi dalla raccolta',
|
||||
'collections.removeFromCollectionConfirm' => ({required Object title}) => 'Rimuovere "${title}" da questa raccolta?',
|
||||
'collections.removedFromCollection' => 'Rimosso dalla raccolta',
|
||||
'collections.removeFromCollectionFailed' => 'Impossibile rimuovere dalla raccolta',
|
||||
|
||||
@@ -452,6 +452,7 @@ class _TranslationsVideoControlsKo implements TranslationsVideoControlsEn {
|
||||
@override String get rotationLockButton => '회전 잠금';
|
||||
@override String get timelineSlider => '타임라인';
|
||||
@override String get volumeSlider => '볼륨 조절';
|
||||
@override String endsAt({required Object time}) => '${time}에 종료';
|
||||
@override String get backButton => '뒤로 가기';
|
||||
@override String get pipFailed => '화면 속 화면 모드를 시작할 수 없습니다';
|
||||
@override late final _TranslationsVideoControlsPipErrorsKo pipErrors = _TranslationsVideoControlsPipErrorsKo._(_root);
|
||||
@@ -1258,6 +1259,7 @@ extension on TranslationsKo {
|
||||
'videoControls.rotationLockButton' => '회전 잠금',
|
||||
'videoControls.timelineSlider' => '타임라인',
|
||||
'videoControls.volumeSlider' => '볼륨 조절',
|
||||
'videoControls.endsAt' => ({required Object time}) => '${time}에 종료',
|
||||
'videoControls.backButton' => '뒤로 가기',
|
||||
'videoControls.pipFailed' => '화면 속 화면 모드를 시작할 수 없습니다',
|
||||
'videoControls.pipErrors.androidVersion' => 'Android 8.0 이상이 필요합니다',
|
||||
@@ -1496,9 +1498,9 @@ extension on TranslationsKo {
|
||||
'watchTogether.controlMode' => '제어 모드',
|
||||
'watchTogether.controlModeQuestion' => '누가 재생을 제어할 수 있나요?',
|
||||
'watchTogether.hostOnly' => '호스트만',
|
||||
'watchTogether.anyone' => '누구나',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'watchTogether.anyone' => '누구나',
|
||||
'watchTogether.hostingSession' => '세션 호스팅',
|
||||
'watchTogether.inSession' => '세션 중',
|
||||
'watchTogether.sessionCode' => '세션 코드',
|
||||
|
||||
@@ -452,6 +452,7 @@ class _TranslationsVideoControlsNl implements TranslationsVideoControlsEn {
|
||||
@override String get rotationLockButton => 'Rotatievergrendeling';
|
||||
@override String get timelineSlider => 'Videotijdlijn';
|
||||
@override String get volumeSlider => 'Volumeniveau';
|
||||
@override String endsAt({required Object time}) => 'Eindigt om ${time}';
|
||||
@override String get backButton => 'Terug';
|
||||
@override String get pipFailed => 'Beeld-in-beeld kon niet worden gestart';
|
||||
@override late final _TranslationsVideoControlsPipErrorsNl pipErrors = _TranslationsVideoControlsPipErrorsNl._(_root);
|
||||
@@ -1258,6 +1259,7 @@ extension on TranslationsNl {
|
||||
'videoControls.rotationLockButton' => 'Rotatievergrendeling',
|
||||
'videoControls.timelineSlider' => 'Videotijdlijn',
|
||||
'videoControls.volumeSlider' => 'Volumeniveau',
|
||||
'videoControls.endsAt' => ({required Object time}) => 'Eindigt om ${time}',
|
||||
'videoControls.backButton' => 'Terug',
|
||||
'videoControls.pipFailed' => 'Beeld-in-beeld kon niet worden gestart',
|
||||
'videoControls.pipErrors.androidVersion' => 'Vereist Android 8.0 of nieuwer',
|
||||
@@ -1496,9 +1498,9 @@ extension on TranslationsNl {
|
||||
'collections.addedToCollection' => 'Toegevoegd aan collectie',
|
||||
'collections.errorAddingToCollection' => 'Fout bij toevoegen aan collectie',
|
||||
'collections.created' => 'Collectie gemaakt',
|
||||
'collections.removeFromCollection' => 'Verwijderen uit collectie',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'collections.removeFromCollection' => 'Verwijderen uit collectie',
|
||||
'collections.removeFromCollectionConfirm' => ({required Object title}) => '"${title}" uit deze collectie verwijderen?',
|
||||
'collections.removedFromCollection' => 'Uit collectie verwijderd',
|
||||
'collections.removeFromCollectionFailed' => 'Verwijderen uit collectie mislukt',
|
||||
|
||||
@@ -452,6 +452,7 @@ class _TranslationsVideoControlsSv implements TranslationsVideoControlsEn {
|
||||
@override String get rotationLockButton => 'Rotationslås';
|
||||
@override String get timelineSlider => 'Videotidslinje';
|
||||
@override String get volumeSlider => 'Volymnivå';
|
||||
@override String endsAt({required Object time}) => 'Slutar ${time}';
|
||||
@override String get backButton => 'Tillbaka';
|
||||
@override String get pipFailed => 'Bild-i-bild kunde inte starta';
|
||||
@override late final _TranslationsVideoControlsPipErrorsSv pipErrors = _TranslationsVideoControlsPipErrorsSv._(_root);
|
||||
@@ -1258,6 +1259,7 @@ extension on TranslationsSv {
|
||||
'videoControls.rotationLockButton' => 'Rotationslås',
|
||||
'videoControls.timelineSlider' => 'Videotidslinje',
|
||||
'videoControls.volumeSlider' => 'Volymnivå',
|
||||
'videoControls.endsAt' => ({required Object time}) => 'Slutar ${time}',
|
||||
'videoControls.backButton' => 'Tillbaka',
|
||||
'videoControls.pipFailed' => 'Bild-i-bild kunde inte starta',
|
||||
'videoControls.pipErrors.androidVersion' => 'Kräver Android 8.0 eller nyare',
|
||||
@@ -1496,9 +1498,9 @@ extension on TranslationsSv {
|
||||
'collections.addedToCollection' => 'Tillagd i samling',
|
||||
'collections.errorAddingToCollection' => 'Fel vid tillägg i samling',
|
||||
'collections.created' => 'Samling skapad',
|
||||
'collections.removeFromCollection' => 'Ta bort från samling',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'collections.removeFromCollection' => 'Ta bort från samling',
|
||||
'collections.removeFromCollectionConfirm' => ({required Object title}) => 'Ta bort "${title}" från denna samling?',
|
||||
'collections.removedFromCollection' => 'Borttagen från samling',
|
||||
'collections.removeFromCollectionFailed' => 'Misslyckades med att ta bort från samling',
|
||||
|
||||
@@ -452,6 +452,7 @@ class _TranslationsVideoControlsZh implements TranslationsVideoControlsEn {
|
||||
@override String get rotationLockButton => '旋转锁定';
|
||||
@override String get timelineSlider => '视频时间轴';
|
||||
@override String get volumeSlider => '音量调节';
|
||||
@override String endsAt({required Object time}) => '${time} 结束';
|
||||
@override String get backButton => '返回';
|
||||
@override String get pipFailed => '画中画启动失败';
|
||||
@override late final _TranslationsVideoControlsPipErrorsZh pipErrors = _TranslationsVideoControlsPipErrorsZh._(_root);
|
||||
@@ -1258,6 +1259,7 @@ extension on TranslationsZh {
|
||||
'videoControls.rotationLockButton' => '旋转锁定',
|
||||
'videoControls.timelineSlider' => '视频时间轴',
|
||||
'videoControls.volumeSlider' => '音量调节',
|
||||
'videoControls.endsAt' => ({required Object time}) => '${time} 结束',
|
||||
'videoControls.backButton' => '返回',
|
||||
'videoControls.pipFailed' => '画中画启动失败',
|
||||
'videoControls.pipErrors.androidVersion' => '需要 Android 8.0 或更高版本',
|
||||
@@ -1496,9 +1498,9 @@ extension on TranslationsZh {
|
||||
'collections.addedToCollection' => '已添加到合集',
|
||||
'collections.errorAddingToCollection' => '添加到合集失败',
|
||||
'collections.created' => '已创建合集',
|
||||
'collections.removeFromCollection' => '从合集移除',
|
||||
_ => null,
|
||||
} ?? switch (path) {
|
||||
'collections.removeFromCollection' => '从合集移除',
|
||||
'collections.removeFromCollectionConfirm' => ({required Object title}) => '将“${title}”从此合集移除?',
|
||||
'collections.removedFromCollection' => '已从合集移除',
|
||||
'collections.removeFromCollectionFailed' => '从合集移除失败',
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
"rotationLockButton": "Rotationslås",
|
||||
"timelineSlider": "Videotidslinje",
|
||||
"volumeSlider": "Volymnivå",
|
||||
"endsAt": "Slutar ${time}",
|
||||
"backButton": "Tillbaka",
|
||||
"pipFailed": "Bild-i-bild kunde inte starta",
|
||||
"pipErrors": {
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
"rotationLockButton": "旋转锁定",
|
||||
"timelineSlider": "视频时间轴",
|
||||
"volumeSlider": "音量调节",
|
||||
"endsAt": "${time} 结束",
|
||||
"backButton": "返回",
|
||||
"pipFailed": "画中画启动失败",
|
||||
"pipErrors": {
|
||||
|
||||
@@ -180,6 +180,14 @@ DurationLocale _getDurationLocale() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats the clock time at which media will finish playing, given the remaining duration.
|
||||
/// Returns a localized time string like "6:12 PM" or "18:12" depending on locale.
|
||||
String formatFinishTime(Duration remaining) {
|
||||
final finishTime = DateTime.now().add(remaining);
|
||||
final formatter = DateFormat.jm(LocaleSettings.currentLocale.languageCode);
|
||||
return formatter.format(finishTime);
|
||||
}
|
||||
|
||||
/// Takes a list of strings and returns one long string with each item in the list concatenated by a bullet
|
||||
String toBulletedString(List<String> parts) {
|
||||
return parts.join(' · ');
|
||||
|
||||
@@ -12,6 +12,7 @@ import '../../models/plex_media_version.dart';
|
||||
import '../../models/plex_metadata.dart';
|
||||
import '../../services/fullscreen_state_manager.dart';
|
||||
import '../../utils/desktop_window_padding.dart';
|
||||
import '../../utils/formatters.dart';
|
||||
import '../../i18n/strings.g.dart';
|
||||
import '../../focus/focusable_wrapper.dart';
|
||||
import '../../services/shader_service.dart';
|
||||
@@ -506,6 +507,30 @@ class DesktopVideoControlsState extends State<DesktopVideoControls> {
|
||||
semanticLabel: t.videoControls.nextButton,
|
||||
),
|
||||
),
|
||||
// Finish time
|
||||
StreamBuilder<Duration>(
|
||||
stream: widget.player.streams.position,
|
||||
initialData: widget.player.state.position,
|
||||
builder: (context, posSnap) {
|
||||
return StreamBuilder<Duration>(
|
||||
stream: widget.player.streams.duration,
|
||||
initialData: widget.player.state.duration,
|
||||
builder: (context, durSnap) {
|
||||
final position = posSnap.data ?? Duration.zero;
|
||||
final duration = durSnap.data ?? Duration.zero;
|
||||
final remaining = duration - position;
|
||||
if (remaining.inSeconds <= 0) return const SizedBox.shrink();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: Text(
|
||||
t.videoControls.endsAt(time: formatFinishTime(remaining)),
|
||||
style: const TextStyle(color: Colors.white70, fontSize: 13),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
const Spacer(),
|
||||
// Volume control
|
||||
VolumeControl(
|
||||
|
||||
@@ -166,6 +166,7 @@ class MobileVideoControls extends StatelessWidget {
|
||||
onSeekEnd: onSeekEnd,
|
||||
horizontalLayout: false,
|
||||
enabled: canControl,
|
||||
showFinishTime: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -33,6 +33,9 @@ class VideoTimelineBar extends StatelessWidget {
|
||||
/// Whether the timeline is enabled for interaction.
|
||||
final bool enabled;
|
||||
|
||||
/// Whether to show the estimated finish time next to the remaining timestamp (mobile).
|
||||
final bool showFinishTime;
|
||||
|
||||
const VideoTimelineBar({
|
||||
super.key,
|
||||
required this.player,
|
||||
@@ -45,6 +48,7 @@ class VideoTimelineBar extends StatelessWidget {
|
||||
this.onKeyEvent,
|
||||
this.onFocusChange,
|
||||
this.enabled = true,
|
||||
this.showFinishTime = false,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -92,7 +96,7 @@ class VideoTimelineBar extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [_buildTimestamp(position), _buildTimestamp(remaining)],
|
||||
children: [_buildTimestamp(position), _buildRemainingTimestamp(remaining)],
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -103,6 +107,14 @@ class VideoTimelineBar extends StatelessWidget {
|
||||
return Text(formatDurationTimestamp(time), style: const TextStyle(color: Colors.white, fontSize: 14));
|
||||
}
|
||||
|
||||
Widget _buildRemainingTimestamp(Duration remaining) {
|
||||
if (!showFinishTime || remaining.inSeconds >= 0) {
|
||||
return _buildTimestamp(remaining);
|
||||
}
|
||||
final text = '${formatDurationTimestamp(remaining)} · ${formatFinishTime(remaining.abs())}';
|
||||
return Text(text, style: const TextStyle(color: Colors.white, fontSize: 14));
|
||||
}
|
||||
|
||||
Widget _buildSlider(Duration position, Duration duration) {
|
||||
return TimelineSlider(
|
||||
position: position,
|
||||
|
||||
Reference in New Issue
Block a user