feat: create video player speed indicator

This commit is contained in:
Trizotto 2025-09-07 15:03:41 +02:00
parent 838c5b3485
commit adefa1e4cd
3 changed files with 97 additions and 0 deletions

View file

@ -1327,5 +1327,13 @@
"type": "int" "type": "int"
} }
} }
},
"speedIndicator": "Playback rate: {speed}",
"@speedIndicator": {
"placeholders": {
"speed": {
"type": "double"
}
}
} }
} }

View file

@ -0,0 +1,87 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:async/async.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:iconsax_plus/iconsax_plus.dart';
import 'package:fladder/providers/settings/video_player_settings_provider.dart';
import 'package:fladder/util/localization_helper.dart';
class VideoPlayerSpeedIndicator extends ConsumerStatefulWidget {
const VideoPlayerSpeedIndicator({super.key});
@override
ConsumerState<ConsumerStatefulWidget> createState() => _VideoPlayerSpeedIndicatorState();
}
class _VideoPlayerSpeedIndicatorState extends ConsumerState<VideoPlayerSpeedIndicator> {
late double currentSpeed = ref.read(playbackRateProvider);
bool showIndicator = false;
late final timer = RestartableTimer(const Duration(seconds: 1), () {
setState(() {
showIndicator = false;
});
});
@override
void dispose() {
showIndicator = false;
timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
ref.listen(
playbackRateProvider,
(previous, next) {
setState(() {
showIndicator = true;
currentSpeed = next;
});
timer.reset();
},
);
return IgnorePointer(
child: AnimatedOpacity(
duration: const Duration(milliseconds: 250),
opacity: showIndicator ? 1 : 0,
child: Center(
child: Container(
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.85),
borderRadius: BorderRadius.circular(16),
),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisSize: MainAxisSize.min,
spacing: 12,
children: [
Transform.rotate(
angle: currentSpeed < 1 ? pi : 0,
child: Icon(speedIcon(currentSpeed)),
),
Text(context.localized.speedIndicator(currentSpeed)),
],
),
),
),
),
),
);
}
}
IconData speedIcon(double value) {
if (value < 1) {
return IconsaxPlusBroken.flash;
}
if (value == 1) {
return IconsaxPlusLinear.flash_slash;
}
return IconsaxPlusLinear.flash;
}

View file

@ -26,6 +26,7 @@ import 'package:fladder/screens/video_player/components/video_player_controls_ex
import 'package:fladder/screens/video_player/components/video_player_options_sheet.dart'; import 'package:fladder/screens/video_player/components/video_player_options_sheet.dart';
import 'package:fladder/screens/video_player/components/video_player_quality_controls.dart'; import 'package:fladder/screens/video_player/components/video_player_quality_controls.dart';
import 'package:fladder/screens/video_player/components/video_player_seek_indicator.dart'; import 'package:fladder/screens/video_player/components/video_player_seek_indicator.dart';
import 'package:fladder/screens/video_player/components/video_player_speed_indicator.dart';
import 'package:fladder/screens/video_player/components/video_player_volume_indicator.dart'; import 'package:fladder/screens/video_player/components/video_player_volume_indicator.dart';
import 'package:fladder/screens/video_player/components/video_progress_bar.dart'; import 'package:fladder/screens/video_player/components/video_progress_bar.dart';
import 'package:fladder/screens/video_player/components/video_volume_slider.dart'; import 'package:fladder/screens/video_player/components/video_volume_slider.dart';
@ -125,6 +126,7 @@ class _DesktopControlsState extends ConsumerState<DesktopControls> {
), ),
const VideoPlayerSeekIndicator(), const VideoPlayerSeekIndicator(),
const VideoPlayerVolumeIndicator(), const VideoPlayerVolumeIndicator(),
const VideoPlayerSpeedIndicator(),
Consumer( Consumer(
builder: (context, ref, child) { builder: (context, ref, child) {
final position = ref.watch(mediaPlaybackProvider.select((value) => value.position)); final position = ref.watch(mediaPlaybackProvider.select((value) => value.position));