refactor: use CustomScrollView in player queue

This commit is contained in:
Kingkor Roy Tirtho 2024-03-28 22:49:40 +06:00
parent ee97aedcfc
commit 044d3b4820

View File

@ -3,11 +3,15 @@ import 'dart:ui';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fuzzywuzzy/fuzzywuzzy.dart'; import 'package:fuzzywuzzy/fuzzywuzzy.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:scroll_to_index/scroll_to_index.dart'; import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:sliver_tools/sliver_tools.dart';
import 'package:spotube/collections/fake.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/shared/fallbacks/not_found.dart'; import 'package:spotube/components/shared/fallbacks/not_found.dart';
import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart'; import 'package:spotube/components/shared/inter_scrollbar/inter_scrollbar.dart';
@ -109,171 +113,168 @@ class PlayerQueue extends HookConsumerWidget {
searchText.value = ''; searchText.value = '';
} }
}, },
child: Column( child: InterScrollbar(
children: [ controller: controller,
if (!floating) child: CustomScrollView(
Container( controller: controller,
height: 5, slivers: [
width: 100, if (!floating)
margin: const EdgeInsets.only(bottom: 5, top: 2), SliverToBoxAdapter(
decoration: BoxDecoration( child: Center(
color: headlineColor, child: Container(
borderRadius: BorderRadius.circular(20), height: 5,
), width: 100,
), margin: const EdgeInsets.only(bottom: 5, top: 2),
Row( decoration: BoxDecoration(
crossAxisAlignment: CrossAxisAlignment.center, color: headlineColor,
mainAxisAlignment: MainAxisAlignment.center, borderRadius: BorderRadius.circular(20),
children: [
if (mediaQuery.mdAndUp || !isSearching.value) ...[
const SizedBox(width: 10),
Text(
context.l10n.tracks_in_queue(tracks.length),
style: TextStyle(
color: headlineColor,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
const Spacer(),
],
if (mediaQuery.mdAndUp || isSearching.value)
TextField(
onChanged: (value) {
searchText.value = value;
},
decoration: InputDecoration(
hintText: context.l10n.search,
isDense: true,
prefixIcon: mediaQuery.smAndDown
? IconButton(
icon: const Icon(
Icons.arrow_back_ios_new_outlined,
),
onPressed: () {
isSearching.value = false;
searchText.value = '';
},
style: IconButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: const Size.square(20),
),
)
: const Icon(SpotubeIcons.filter),
constraints: BoxConstraints(
maxHeight: 40,
maxWidth: mediaQuery.smAndDown
? mediaQuery.size.width - 40
: 300,
), ),
), ),
)
else
IconButton.filledTonal(
icon: const Icon(SpotubeIcons.filter),
onPressed: () {
isSearching.value = !isSearching.value;
},
), ),
if (mediaQuery.mdAndUp || !isSearching.value) ...[ ),
const SizedBox(width: 10), SliverAppBar(
FilledButton( floating: true,
style: FilledButton.styleFrom( pinned: false,
backgroundColor: snap: false,
theme.scaffoldBackgroundColor.withOpacity(0.5), backgroundColor: Colors.transparent,
foregroundColor: theme.textTheme.headlineSmall?.color, elevation: 0,
), automaticallyImplyLeading: !isSearching.value,
child: Row( title: BackdropFilter(
children: [ filter: ImageFilter.blur(
const Icon(SpotubeIcons.playlistRemove), sigmaX: 10,
const SizedBox(width: 5), sigmaY: 10,
Text(context.l10n.clear_all),
],
),
onPressed: () {
playlistNotifier.stop();
Navigator.of(context).pop();
},
), ),
const SizedBox(width: 10), child: SizedBox(
], height: kToolbarHeight,
], child: mediaQuery.mdAndUp || !isSearching.value
), ? Align(
const SizedBox(height: 10), alignment: Alignment.centerLeft,
if (!isSearching.value && searchText.value.isEmpty) child: Text(
Flexible( context.l10n.tracks_in_queue(tracks.length),
child: ReorderableListView.builder( style: TextStyle(
onReorder: (oldIndex, newIndex) { color: headlineColor,
playlistNotifier.moveTrack(oldIndex, newIndex); fontWeight: FontWeight.bold,
}, fontSize: 18,
scrollController: controller, ),
itemCount: tracks.length,
shrinkWrap: true,
buildDefaultDragHandles: false,
onReorderStart: (index) {
HapticFeedback.selectionClick();
},
onReorderEnd: (index) {
HapticFeedback.selectionClick();
},
itemBuilder: (context, i) {
final track = tracks.elementAt(i);
return AutoScrollTag(
key: ValueKey(i),
controller: controller,
index: i,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0),
child: TrackTile(
index: i,
track: track,
onTap: () async {
if (playlist.activeTrack?.id == track.id) {
return;
}
await playlistNotifier.jumpToTrack(track);
},
leadingActions: [
ReorderableDragStartListener(
index: i,
child: const Icon(SpotubeIcons.dragHandle),
), ),
], )
), : null,
),
);
},
),
)
else
Flexible(
child: InterScrollbar(
controller: controller,
child: ListView.builder(
controller: controller,
itemCount: filteredTracks.length,
itemBuilder: (context, i) {
final track = filteredTracks.elementAt(i);
return Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0),
child: TrackTile(
index: i,
track: track,
onTap: () async {
if (playlist.activeTrack?.id == track.id) {
return;
}
await playlistNotifier.jumpToTrack(track);
},
),
);
},
), ),
), ),
actions: [
if (mediaQuery.mdAndUp || isSearching.value)
TextField(
onChanged: (value) {
searchText.value = value;
},
decoration: InputDecoration(
hintText: context.l10n.search,
isDense: true,
prefixIcon: mediaQuery.smAndDown
? IconButton(
icon: const Icon(
Icons.arrow_back_ios_new_outlined,
),
onPressed: () {
isSearching.value = false;
searchText.value = '';
},
style: IconButton.styleFrom(
padding: EdgeInsets.zero,
minimumSize: const Size.square(20),
),
)
: const Icon(SpotubeIcons.filter),
constraints: BoxConstraints(
maxHeight: 40,
maxWidth: mediaQuery.smAndDown
? mediaQuery.size.width - 40
: 300,
),
),
)
else
IconButton.filledTonal(
icon: const Icon(SpotubeIcons.filter),
onPressed: () {
isSearching.value = !isSearching.value;
},
),
if (mediaQuery.mdAndUp || !isSearching.value) ...[
const SizedBox(width: 10),
FilledButton(
style: FilledButton.styleFrom(
backgroundColor:
theme.scaffoldBackgroundColor.withOpacity(0.5),
foregroundColor:
theme.textTheme.headlineSmall?.color,
),
child: Row(
children: [
const Icon(SpotubeIcons.playlistRemove),
const SizedBox(width: 5),
Text(context.l10n.clear_all),
],
),
onPressed: () {
playlistNotifier.stop();
Navigator.of(context).pop();
},
),
const SizedBox(width: 10),
],
],
), ),
], const SliverGap(10),
SliverReorderableList(
onReorder: (oldIndex, newIndex) {
playlistNotifier.moveTrack(oldIndex, newIndex);
},
itemCount: filteredTracks.length,
onReorderStart: (index) {
HapticFeedback.selectionClick();
},
onReorderEnd: (index) {
HapticFeedback.selectionClick();
},
itemBuilder: (context, i) {
final track = filteredTracks.elementAt(i);
return AutoScrollTag(
key: ValueKey<int>(i),
controller: controller,
index: i,
child: Material(
color: Colors.transparent,
child: TrackTile(
index: i,
track: track,
onTap: () async {
if (playlist.activeTrack?.id == track.id) {
return;
}
await playlistNotifier.jumpToTrack(track);
},
leadingActions: [
if (!isSearching.value &&
searchText.value.isEmpty)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: ReorderableDragStartListener(
index: i,
child: const Icon(
SpotubeIcons.dragHandle,
),
),
),
],
),
),
);
},
),
const SliverGap(100),
],
),
), ),
), ),
), ),