mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
feat: volume slider mouse scroll and preference for Rotating Album Art #255
This commit is contained in:
parent
6d4c6b1738
commit
edb6f3cd1c
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:spotube/components/Player/PlayerActions.dart';
|
import 'package:spotube/components/Player/PlayerActions.dart';
|
||||||
@ -122,23 +123,36 @@ class Player extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}, [playback.volume]);
|
}, [playback.volume]);
|
||||||
return Slider.adaptive(
|
return Listener(
|
||||||
min: 0,
|
onPointerSignal: (event) async {
|
||||||
max: 1,
|
if (event is PointerScrollEvent) {
|
||||||
value: volume.value,
|
if (event.scrollDelta.dy > 0) {
|
||||||
onChanged: (v) {
|
final value = volume.value - .2;
|
||||||
volume.value = v;
|
playback.setVolume(value < 0 ? 0 : value);
|
||||||
},
|
} else {
|
||||||
onChangeEnd: (value) async {
|
final value = volume.value + .2;
|
||||||
try {
|
playback.setVolume(value > 1 ? 1 : value);
|
||||||
// You don't really need to know why but this
|
}
|
||||||
// way it works only
|
|
||||||
await playback.setVolume(value);
|
|
||||||
await playback.setVolume(value);
|
|
||||||
} catch (e, stack) {
|
|
||||||
logger.e("onChange", e, stack);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
child: Slider.adaptive(
|
||||||
|
min: 0,
|
||||||
|
max: 1,
|
||||||
|
value: volume.value,
|
||||||
|
onChanged: (v) {
|
||||||
|
volume.value = v;
|
||||||
|
},
|
||||||
|
onChangeEnd: (value) async {
|
||||||
|
try {
|
||||||
|
// You don't really need to know why but this
|
||||||
|
// way it works only
|
||||||
|
await playback.setVolume(value);
|
||||||
|
await playback.setVolume(value);
|
||||||
|
} catch (e, stack) {
|
||||||
|
logger.e("onChange", e, stack);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
@ -15,6 +14,7 @@ import 'package:spotube/hooks/useBreakpoints.dart';
|
|||||||
import 'package:spotube/hooks/useCustomStatusBarColor.dart';
|
import 'package:spotube/hooks/useCustomStatusBarColor.dart';
|
||||||
import 'package:spotube/hooks/usePaletteColor.dart';
|
import 'package:spotube/hooks/usePaletteColor.dart';
|
||||||
import 'package:spotube/provider/Playback.dart';
|
import 'package:spotube/provider/Playback.dart';
|
||||||
|
import 'package:spotube/provider/UserPreferences.dart';
|
||||||
import 'package:spotube/utils/type_conversion_utils.dart';
|
import 'package:spotube/utils/type_conversion_utils.dart';
|
||||||
|
|
||||||
class PlayerView extends HookConsumerWidget {
|
class PlayerView extends HookConsumerWidget {
|
||||||
@ -28,6 +28,9 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
(value) => value.track,
|
(value) => value.track,
|
||||||
));
|
));
|
||||||
final breakpoint = useBreakpoints();
|
final breakpoint = useBreakpoints();
|
||||||
|
final canRotate = ref.watch(
|
||||||
|
userPreferencesProvider.select((s) => s.rotatingAlbumArt),
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
if (breakpoint.isMoreThan(Breakpoints.md)) {
|
if (breakpoint.isMoreThan(Breakpoints.md)) {
|
||||||
@ -108,23 +111,46 @@ class PlayerView extends HookConsumerWidget {
|
|||||||
final controller = useAnimationController(
|
final controller = useAnimationController(
|
||||||
duration: const Duration(seconds: 10),
|
duration: const Duration(seconds: 10),
|
||||||
vsync: ticker,
|
vsync: ticker,
|
||||||
)..repeat();
|
);
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() {
|
||||||
|
controller.repeat();
|
||||||
|
if (!canRotate) controller.stop();
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
[controller],
|
||||||
|
);
|
||||||
return RotationTransition(
|
return RotationTransition(
|
||||||
turns: Tween(begin: 0.0, end: 1.0).animate(controller),
|
turns: Tween(begin: 0.0, end: 1.0).animate(controller),
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
border: Border.all(
|
border: canRotate
|
||||||
color: paletteColor.titleTextColor,
|
? Border.all(
|
||||||
width: 2,
|
color: paletteColor.titleTextColor,
|
||||||
),
|
width: 2,
|
||||||
shape: BoxShape.circle,
|
)
|
||||||
),
|
: null,
|
||||||
child: CircleAvatar(
|
borderRadius:
|
||||||
backgroundImage:
|
!canRotate ? BorderRadius.circular(15) : null,
|
||||||
UniversalImage.imageProvider(albumArt),
|
shape:
|
||||||
radius: MediaQuery.of(context).size.width *
|
canRotate ? BoxShape.circle : BoxShape.rectangle,
|
||||||
(breakpoint.isSm ? 0.4 : 0.3),
|
|
||||||
),
|
),
|
||||||
|
child: !canRotate
|
||||||
|
? ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
child: UniversalImage(
|
||||||
|
path: albumArt,
|
||||||
|
width: MediaQuery.of(context).size.width *
|
||||||
|
(breakpoint.isSm ? 0.8 : 0.5),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: CircleAvatar(
|
||||||
|
backgroundImage:
|
||||||
|
UniversalImage.imageProvider(albumArt),
|
||||||
|
radius: MediaQuery.of(context).size.width *
|
||||||
|
(breakpoint.isSm ? 0.4 : 0.3),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
|
@ -231,6 +231,17 @@ class Settings extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
onTap: pickColorScheme(ColorSchemeType.background),
|
onTap: pickColorScheme(ColorSchemeType.background),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(Icons.album_rounded),
|
||||||
|
title: const Text("Rotating Album Art"),
|
||||||
|
trailing: Switch.adaptive(
|
||||||
|
activeColor: Theme.of(context).primaryColor,
|
||||||
|
value: preferences.rotatingAlbumArt,
|
||||||
|
onChanged: (state) {
|
||||||
|
preferences.setRotatingAlbumArt(state);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
const Text(
|
const Text(
|
||||||
" Playback",
|
" Playback",
|
||||||
style:
|
style:
|
||||||
|
@ -36,6 +36,7 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
String downloadLocation;
|
String downloadLocation;
|
||||||
|
|
||||||
LayoutMode layoutMode;
|
LayoutMode layoutMode;
|
||||||
|
bool rotatingAlbumArt;
|
||||||
|
|
||||||
UserPreferences({
|
UserPreferences({
|
||||||
required this.geniusAccessToken,
|
required this.geniusAccessToken,
|
||||||
@ -51,6 +52,7 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
this.audioQuality = AudioQuality.high,
|
this.audioQuality = AudioQuality.high,
|
||||||
this.skipSponsorSegments = true,
|
this.skipSponsorSegments = true,
|
||||||
this.downloadLocation = "",
|
this.downloadLocation = "",
|
||||||
|
this.rotatingAlbumArt = true,
|
||||||
}) : super() {
|
}) : super() {
|
||||||
if (downloadLocation.isEmpty) {
|
if (downloadLocation.isEmpty) {
|
||||||
_getDefaultDownloadDirectory().then(
|
_getDefaultDownloadDirectory().then(
|
||||||
@ -140,6 +142,12 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
updatePersistence();
|
updatePersistence();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setRotatingAlbumArt(bool should) {
|
||||||
|
rotatingAlbumArt = should;
|
||||||
|
notifyListeners();
|
||||||
|
updatePersistence();
|
||||||
|
}
|
||||||
|
|
||||||
Future<String> _getDefaultDownloadDirectory() async {
|
Future<String> _getDefaultDownloadDirectory() async {
|
||||||
if (kIsAndroid) return "/storage/emulated/0/Download/Spotube";
|
if (kIsAndroid) return "/storage/emulated/0/Download/Spotube";
|
||||||
|
|
||||||
@ -182,6 +190,7 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
(mode) => mode.name == map["layoutMode"],
|
(mode) => mode.name == map["layoutMode"],
|
||||||
orElse: () => kIsDesktop ? LayoutMode.extended : LayoutMode.compact,
|
orElse: () => kIsDesktop ? LayoutMode.extended : LayoutMode.compact,
|
||||||
);
|
);
|
||||||
|
rotatingAlbumArt = map["rotatingAlbumArt"] ?? rotatingAlbumArt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -200,6 +209,7 @@ class UserPreferences extends PersistedChangeNotifier {
|
|||||||
"skipSponsorSegments": skipSponsorSegments,
|
"skipSponsorSegments": skipSponsorSegments,
|
||||||
"downloadLocation": downloadLocation,
|
"downloadLocation": downloadLocation,
|
||||||
"layoutMode": layoutMode.name,
|
"layoutMode": layoutMode.name,
|
||||||
|
"rotatingAlbumArt": rotatingAlbumArt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user