mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-09 09:31:10 +02:00
refactor(library): share the track selection bar and delete action
The Library list's multi-select toolbar (FLAC upgrade, re-enrich, convert, ReplayGain add/remove, delete) becomes LibraryTrackSelectionBar, and its delete flow becomes deleteLibraryTracks. The Library list renders and behaves as before; other screens can now offer the same actions.
This commit is contained in:
1 parent
a549671c4e
commit
86cf860288
5 files changed
+210
-172
No files matched your search
@@ -0,0 +1,130 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/widgets/app_action_button.dart';
|
||||
import 'package:spotiflac_android/widgets/selection_action_button.dart';
|
||||
import 'package:spotiflac_android/widgets/selection_bottom_bar.dart';
|
||||
|
||||
/// Multi-select toolbar for Library tracks: FLAC upgrade (local selections
|
||||
/// only), re-enrich, convert, ReplayGain add/remove and delete. Shared by the
|
||||
/// Library list and Library search results so both expose the same actions.
|
||||
class LibraryTrackSelectionBar extends StatelessWidget {
|
||||
const LibraryTrackSelectionBar({
|
||||
super.key,
|
||||
required this.selectedCount,
|
||||
required this.allSelected,
|
||||
required this.onClose,
|
||||
required this.onToggleSelectAll,
|
||||
required this.bottomPadding,
|
||||
required this.onReEnrich,
|
||||
required this.onConvert,
|
||||
required this.onReplayGain,
|
||||
required this.onDelete,
|
||||
this.flacEligibleCount = 0,
|
||||
this.onQueueFlac,
|
||||
});
|
||||
|
||||
final int selectedCount;
|
||||
final bool allSelected;
|
||||
final VoidCallback onClose;
|
||||
final VoidCallback onToggleSelectAll;
|
||||
final double bottomPadding;
|
||||
final VoidCallback onReEnrich;
|
||||
final VoidCallback onConvert;
|
||||
final void Function({required bool remove}) onReplayGain;
|
||||
final VoidCallback onDelete;
|
||||
|
||||
/// FLAC upgrade is offered when this is positive and [onQueueFlac] is set.
|
||||
final int flacEligibleCount;
|
||||
final VoidCallback? onQueueFlac;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final hasSelection = selectedCount > 0;
|
||||
return SelectionBottomBar(
|
||||
selectedCount: selectedCount,
|
||||
allSelected: allSelected,
|
||||
onClose: onClose,
|
||||
onToggleSelectAll: onToggleSelectAll,
|
||||
bottomPadding: bottomPadding,
|
||||
children: [
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
const spacing = 8.0;
|
||||
final columns = (constraints.maxWidth / 320).floor().clamp(2, 4);
|
||||
final itemWidth =
|
||||
(constraints.maxWidth - spacing * (columns - 1)) / columns;
|
||||
final actions = <Widget>[
|
||||
if (onQueueFlac != null && flacEligibleCount > 0)
|
||||
SelectionActionButton(
|
||||
icon: Icons.download_for_offline_outlined,
|
||||
label: '${context.l10n.queueFlacAction} ($flacEligibleCount)',
|
||||
onPressed: onQueueFlac,
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
SelectionActionButton(
|
||||
icon: Icons.auto_fix_high_outlined,
|
||||
label: '${context.l10n.trackReEnrich} ($selectedCount)',
|
||||
onPressed: hasSelection ? onReEnrich : null,
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
SelectionActionButton(
|
||||
icon: Icons.swap_horiz,
|
||||
label: context.l10n.selectionConvertCount(selectedCount),
|
||||
onPressed: hasSelection ? onConvert : null,
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
for (final remove in [false, true])
|
||||
SelectionActionButton(
|
||||
icon: remove ? Icons.remove_circle_outline : Icons.graphic_eq,
|
||||
label: remove
|
||||
? context.l10n.selectionRemoveReplayGainCount(
|
||||
selectedCount,
|
||||
)
|
||||
: context.l10n.selectionReplayGainCount(selectedCount),
|
||||
onPressed: hasSelection
|
||||
? () => onReplayGain(remove: remove)
|
||||
: null,
|
||||
colorScheme: colorScheme,
|
||||
),
|
||||
];
|
||||
return Wrap(
|
||||
spacing: spacing,
|
||||
runSpacing: spacing,
|
||||
children: [
|
||||
for (final action in actions)
|
||||
SizedBox(width: itemWidth, child: action),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: AppActionButton(
|
||||
isDestructive: true,
|
||||
onPressed: hasSelection ? onDelete : null,
|
||||
icon: const Icon(Icons.delete_outline),
|
||||
label: Text(
|
||||
hasSelection
|
||||
? context.l10n.selectionDeleteTracksCount(selectedCount)
|
||||
: context.l10n.selectionSelectToDelete,
|
||||
),
|
||||
style: FilledButton.styleFrom(
|
||||
backgroundColor: hasSelection
|
||||
? colorScheme.error
|
||||
: colorScheme.surfaceContainerHighest,
|
||||
foregroundColor: hasSelection
|
||||
? colorScheme.onError
|
||||
: colorScheme.onSurfaceVariant,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user