feat: implement basic seekbar

This commit is contained in:
Matyáš Caras 2024-05-28 19:59:09 +02:00
parent 2ef04267d7
commit b1db0059eb
Signed by: hernik
GPG key ID: 2A3175F98820C5C6
5 changed files with 156 additions and 45 deletions

View file

@ -26,11 +26,19 @@ class AudioPlayerService {
/// Registers listeners on the player /// Registers listeners on the player
void setup() { void setup() {
if (_setUp) return; if (_setUp) return;
_player.createPositionStream().listen((d) {
if (!isPlaying) return;
progressNotifier.value = [
d.inSeconds,
_songList[_player.currentIndex!].duration,
];
});
_player.currentIndexStream.listen((index) { _player.currentIndexStream.listen((index) {
logger.d("Done fired"); logger.d("Done fired");
songNotifier.value = index == null ? null : _songList[index]; songNotifier.value = index == null ? null : _songList[index];
_setColorScheme(); _setColorScheme();
}); });
_setUp = true; _setUp = true;
} }
@ -70,10 +78,16 @@ class AudioPlayerService {
logger.d("Playing ${_player.currentIndex}"); logger.d("Playing ${_player.currentIndex}");
} }
/// Moves to the specified number of seconds
Future<void> seek(double position) async {
await _player.seek(Duration(seconds: position.toInt()));
}
/// Sets color scheme from image /// Sets color scheme from image
Future<void> _setColorScheme() async { Future<void> _setColorScheme() async {
if (AudioPlayerService().song == null) { if (AudioPlayerService().song == null) {
themeNotifier.value = ColorScheme.fromSeed(seedColor: Colors.deepPurple); themeNotifier.value = ColorScheme.fromSeed(seedColor: Colors.deepPurple);
return;
} }
themeNotifier.value = await ColorScheme.fromImageProvider( themeNotifier.value = await ColorScheme.fromImageProvider(
provider: CachedNetworkImageProvider( provider: CachedNetworkImageProvider(

View file

@ -35,6 +35,9 @@ final ValueNotifier<ColorScheme> themeNotifier =
/// Notifier to change theme from inside the app /// Notifier to change theme from inside the app
final ValueNotifier<Song?> songNotifier = ValueNotifier(null); final ValueNotifier<Song?> songNotifier = ValueNotifier(null);
/// Notifier for the song progress bar
final ValueNotifier<List<int>> progressNotifier = ValueNotifier([0, 1]);
/// Main app class /// Main app class
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
/// Main app class /// Main app class
@ -58,8 +61,11 @@ class MyApp extends StatelessWidget {
return Stack( return Stack(
children: [ children: [
child!, child!,
Player( Material(
key: playerKey, type: MaterialType.transparency,
child: Player(
key: playerKey,
),
), ),
], ],
); );

View file

@ -1,6 +1,7 @@
import 'package:auto_size_text/auto_size_text.dart'; import 'package:auto_size_text/auto_size_text.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_xlider/flutter_xlider.dart';
import 'package:ocarina/api/audio/audioplayer_service.dart'; import 'package:ocarina/api/audio/audioplayer_service.dart';
import 'package:ocarina/api/subsonic/song.dart'; import 'package:ocarina/api/subsonic/song.dart';
import 'package:ocarina/main.dart'; import 'package:ocarina/main.dart';
@ -208,57 +209,138 @@ class PlayerState extends State<Player> {
), ),
), ),
), ),
SizedBox( Container(
// Full player controls here
height: 90.h, height: 90.h,
color: Theme.of(context).colorScheme.primaryContainer,
width: 100.w, width: 100.w,
child: Center( child: Center(
child: Column( child: Column(
children: [ children: [
ClipRRect( const SizedBox(
child: (AudioPlayerService().song == null) height: 30,
? ColoredBox( ),
color: Theme.of(context) SizedBox(
.colorScheme width: 80.w,
.primaryContainer, height: 80.w,
child: Center( child: ClipRRect(
child: Icon( // borderRadius: BorderRadius.circular(16),
Icons.music_note, child: (AudioPlayerService().song == null)
color: Theme.of(context) ? ColoredBox(
.colorScheme color: Theme.of(context)
.onPrimaryContainer, .colorScheme
), .primaryContainer,
), child: Center(
) child: Icon(
: (CachedNetworkImage( Icons.music_note,
cacheKey: AudioPlayerService().song!.coverArtId, color: Theme.of(context)
imageUrl: .colorScheme
AudioPlayerService().song!.coverArtUrl, .onPrimaryContainer,
placeholder: (c, d) => Shimmer.fromColors(
baseColor: Colors.grey.shade300,
highlightColor: Colors.grey.shade100,
child: Container(
color: Colors.grey,
),
),
errorWidget: (c, _, __) {
logger
..e(_)
..e(__);
return ColoredBox(
color: Theme.of(context)
.colorScheme
.primaryContainer,
child: Center(
child: Icon(
Icons.music_note,
color: Theme.of(context)
.colorScheme
.onPrimaryContainer,
),
), ),
),
)
: (CachedNetworkImage(
cacheKey:
AudioPlayerService().song!.coverArtId,
imageUrl:
AudioPlayerService().song!.coverArtUrl,
placeholder: (c, d) => Shimmer.fromColors(
baseColor: Colors.grey.shade300,
highlightColor: Colors.grey.shade100,
child: Container(
color: Colors.grey,
),
),
errorWidget: (c, _, __) {
logger
..e(_)
..e(__);
return ColoredBox(
color: Theme.of(context)
.colorScheme
.primaryContainer,
child: Center(
child: Icon(
Icons.music_note,
color: Theme.of(context)
.colorScheme
.onPrimaryContainer,
),
),
);
},
)),
),
),
const SizedBox(
height: 10,
),
SizedBox(
width: 70.w,
height: 40,
child: AutoSizeText(
AudioPlayerService().song?.title ?? "Nothing",
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.sp,
),
overflowReplacement: TextScroll(
AudioPlayerService().song?.title ?? "Nothing",
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20.sp,
),
),
),
),
SizedBox(
width: 70.w,
height: 50,
child: AutoSizeText(
AudioPlayerService().song?.artistName ?? "Nobody",
style: TextStyle(fontSize: 16.sp),
textAlign: TextAlign.center,
overflowReplacement: TextScroll(
AudioPlayerService().song?.artistName ?? "Nobody",
style: TextStyle(
fontSize: 16.sp,
),
),
),
),
const SizedBox(
height: 10,
),
SizedBox(
width: 60.w,
height: 40,
child: Overlay(
initialEntries: [
OverlayEntry(
builder: (c) => ValueListenableBuilder<List<int>>(
valueListenable: progressNotifier,
builder: (c, v, _) {
return FlutterSlider(
values: [v[0].toDouble()],
max: v[1].toDouble(),
min: 0,
onDragCompleted:
(handlerIndex, lowerValue, upperValue) {
if (AudioPlayerService().song == null) {
return;
}
logger.d(lowerValue);
AudioPlayerService()
.seek(lowerValue as double);
},
); );
}, },
)), ),
),
],
),
), ),
], ],
), ),

View file

@ -368,6 +368,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_xlider:
dependency: "direct main"
description:
name: flutter_xlider
sha256: b83da229b8a2153adeefc5d9e08e0060689c8dc2187b30e3502cf67c1a6495be
url: "https://pub.dev"
source: hosted
version: "3.5.0"
frontend_server_client: frontend_server_client:
dependency: transitive dependency: transitive
description: description:

View file

@ -55,6 +55,7 @@ dependencies:
shared_preferences: ^2.2.3 shared_preferences: ^2.2.3
dynamic_color: ^1.7.0 dynamic_color: ^1.7.0
cached_network_image: ^3.3.1 cached_network_image: ^3.3.1
flutter_xlider: ^3.5.0
dev_dependencies: dev_dependencies:
build_runner: ^2.4.9 build_runner: ^2.4.9