Skip to content

Commit

Permalink
Merge pull request #594 from lymnyx/playback-speed
Browse files Browse the repository at this point in the history
Add playback speed option to song menu
  • Loading branch information
Chaphasilor authored May 3, 2024
2 parents 6967675 + e84e953 commit eb0dde9
Show file tree
Hide file tree
Showing 21 changed files with 1,099 additions and 156 deletions.
161 changes: 161 additions & 0 deletions lib/components/AlbumScreen/preset_chip.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:finamp/services/queue_service.dart';
import 'package:get_it/get_it.dart';

const _radius = Radius.circular(4);
const _borderRadius = BorderRadius.all(_radius);
final _defaultBackgroundColour = Colors.white.withOpacity(0.1);
const _spacing = 8.0;

enum PresetTypes {
speed,
}

class PresetChips extends StatefulWidget {
const PresetChips({
Key? key,
required this.type,
required this.values,
required this.activeValue,
this.onTap,
this.mainColour,
this.onPresetSelected,
this.chipWidth = 64.0,
this.chipHeight = 44.0,
}) : super(key: key);

final PresetTypes type;

final List<double> values;
final double activeValue;
final Function()? onTap;
final Color? mainColour; // used for different background colours
final Function()? onPresetSelected;
final double chipWidth;
final double chipHeight;

@override
State<PresetChips> createState() => _PresetChipsState();
}

class _PresetChipsState extends State<PresetChips> {
final _queueService = GetIt.instance<QueueService>();
final _controller = ScrollController();
bool scrolledAlready = false;

void scrollToActivePreset(double currentValue, double maxWidth) {
if (!_controller.hasClients) return;
var offset = widget.chipWidth * widget.values.indexOf(currentValue) +
widget.chipWidth / 2 -
maxWidth / 2 -
_spacing / 2;

offset = min(max(0, offset),
widget.chipWidth * (widget.values.length) - maxWidth - _spacing);

_controller.animateTo(
offset,
duration: Duration(milliseconds: 350),
curve: Curves.easeOutCubic,
);
}

PresetChip generatePresetChip(value, BoxConstraints constraints) {
// Scroll to the active preset
if (value == widget.activeValue) {
if (scrolledAlready) {
scrollToActivePreset(value, constraints.maxWidth);
} else {
Future.delayed(Duration(milliseconds: 200), () {
scrollToActivePreset(value, constraints.maxWidth);
});
scrolledAlready = true;
}
}

final stringValue = "x$value";

return PresetChip(
value: stringValue,
backgroundColour:
widget.mainColour?.withOpacity(value == widget.activeValue
? 0.6
: (value == 1.0)
? 0.3
: 0.1),
isSelected: value == widget.activeValue,
isPresetDefault: value == 1.0,
width: widget.chipWidth,
height: widget.chipHeight,
onTap: () {
setState(() {});
_queueService.playbackSpeed = value;
widget.onPresetSelected?.call();
},
);
}

@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
controller: _controller,
scrollDirection: Axis.horizontal,
child: Wrap(
spacing: _spacing,
crossAxisAlignment: WrapCrossAlignment.center,
children: List.generate(widget.values.length,
(index) => generatePresetChip(widget.values[index], constraints)),
),
);
});
}
}

class PresetChip extends StatelessWidget {
const PresetChip({
Key? key,
required this.width,
required this.height,
this.value = "",
this.onTap,
this.backgroundColour,
this.isSelected,
this.isPresetDefault,
}) : super(key: key);

final double width;
final double height;
final String value;
final void Function()? onTap;
final Color? backgroundColour;
final bool? isSelected;
final bool? isPresetDefault;

@override
Widget build(BuildContext context) {
final backgroundColor = backgroundColour ?? _defaultBackgroundColour;
final color = Theme.of(context).textTheme.bodySmall?.color ?? Colors.white;

return TextButton(
style: TextButton.styleFrom(
backgroundColor: backgroundColor,
shape: const RoundedRectangleBorder(borderRadius: _borderRadius),
minimumSize: Size(width, height),
padding: const EdgeInsets.symmetric(horizontal: 2.0),
visualDensity: VisualDensity.compact,
),
onPressed: onTap,
child: Text(
value,
style: TextStyle(
color: color,
overflow: TextOverflow.visible,
fontWeight: isSelected! ? FontWeight.w800 : FontWeight.normal,
),
softWrap: false,
),
);
}
}
13 changes: 7 additions & 6 deletions lib/components/AlbumScreen/song_list_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ class _SongListTileState extends ConsumerState<SongListTile>
if (widget.showPlayCount)
TextSpan(
text:
"· ${AppLocalizations.of(context)!.playCountValue(widget.item.userData?.playCount ?? 0)}",
" · ${AppLocalizations.of(context)!.playCountValue(widget.item.userData?.playCount ?? 0)}",
style:
TextStyle(color: Theme.of(context).disabledColor),
),
Expand Down Expand Up @@ -358,11 +358,12 @@ class _SongListTileState extends ConsumerState<SongListTile>
if (!mounted) return false;

GlobalSnackbar.message(
(scaffold) => FinampSettingsHelper.finampSettings.swipeInsertQueueNext
? AppLocalizations.of(scaffold)!
.confirmAddToNextUp("track")
: AppLocalizations.of(scaffold)!
.confirmAddToQueue("track"),
(scaffold) =>
FinampSettingsHelper.finampSettings.swipeInsertQueueNext
? AppLocalizations.of(scaffold)!
.confirmAddToNextUp("track")
: AppLocalizations.of(scaffold)!
.confirmAddToQueue("track"),
isConfirmation: true,
);

Expand Down
Loading

0 comments on commit eb0dde9

Please sign in to comment.