mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 16:05:18 +00:00
feat: adaptive popup and bottom sheet list widget
This commit is contained in:
parent
d88d287fc5
commit
ddc1c5f373
146
lib/components/shared/adaptive/adaptive_pop_sheet_list.dart
Normal file
146
lib/components/shared/adaptive/adaptive_pop_sheet_list.dart
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
|
import 'package:spotube/extensions/constrains.dart';
|
||||||
|
|
||||||
|
class PopSheetEntry<T> {
|
||||||
|
final T? value;
|
||||||
|
final VoidCallback? onTap;
|
||||||
|
final Widget child;
|
||||||
|
final bool enabled;
|
||||||
|
|
||||||
|
const PopSheetEntry({
|
||||||
|
required this.child,
|
||||||
|
this.value,
|
||||||
|
this.onTap,
|
||||||
|
this.enabled = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An adaptive widget that shows a [PopupMenuButton] when screen size is above
|
||||||
|
/// or equal to 640px
|
||||||
|
/// In smaller screen, a [IconButton] with a [showModalBottomSheet] is shown
|
||||||
|
class AdaptivePopSheetList<T> extends StatelessWidget {
|
||||||
|
final List<PopSheetEntry<T>> children;
|
||||||
|
final Widget? icon;
|
||||||
|
final Widget? child;
|
||||||
|
final bool useRootNavigator;
|
||||||
|
|
||||||
|
final List<Widget>? headings;
|
||||||
|
final String? tooltip;
|
||||||
|
final ValueChanged<T>? onSelected;
|
||||||
|
|
||||||
|
final BorderRadius borderRadius;
|
||||||
|
|
||||||
|
const AdaptivePopSheetList({
|
||||||
|
super.key,
|
||||||
|
required this.children,
|
||||||
|
this.icon,
|
||||||
|
this.child,
|
||||||
|
this.useRootNavigator = true,
|
||||||
|
this.headings,
|
||||||
|
this.onSelected,
|
||||||
|
this.borderRadius = const BorderRadius.all(Radius.circular(999)),
|
||||||
|
this.tooltip,
|
||||||
|
}) : assert(
|
||||||
|
icon != null || child != null,
|
||||||
|
'Either icon or child must be provided',
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final mediaQuery = MediaQuery.of(context);
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
|
||||||
|
if (mediaQuery.mdAndUp) {
|
||||||
|
return PopupMenuButton(
|
||||||
|
icon: icon,
|
||||||
|
tooltip: tooltip,
|
||||||
|
child: IgnorePointer(child: child),
|
||||||
|
itemBuilder: (context) => children
|
||||||
|
.map(
|
||||||
|
(item) => PopupMenuItem(
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: ListTile(
|
||||||
|
enabled: item.enabled,
|
||||||
|
onTap: () {
|
||||||
|
item.onTap?.call();
|
||||||
|
Navigator.pop(context);
|
||||||
|
if (item.value != null) {
|
||||||
|
onSelected?.call(item.value as T);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: item.child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void showSheet() {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: useRootNavigator,
|
||||||
|
builder: (context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: DefaultTextStyle(
|
||||||
|
style: theme.textTheme.titleMedium!,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
if (headings != null) ...[
|
||||||
|
...headings!,
|
||||||
|
Divider(
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
thickness: 0.3,
|
||||||
|
endIndent: 16,
|
||||||
|
indent: 16,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
...children.map(
|
||||||
|
(item) => ListTile(
|
||||||
|
onTap: () {
|
||||||
|
item.onTap?.call();
|
||||||
|
Navigator.pop(context);
|
||||||
|
if (item.value != null) {
|
||||||
|
onSelected?.call(item.value as T);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled: item.enabled,
|
||||||
|
title: item.child,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (child != null) {
|
||||||
|
return Tooltip(
|
||||||
|
message: tooltip ?? '',
|
||||||
|
child: InkWell(
|
||||||
|
onTap: showSheet,
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
child: IgnorePointer(child: child),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return IconButton(
|
||||||
|
icon: icon ?? const Icon(SpotubeIcons.moreVertical),
|
||||||
|
tooltip: tooltip,
|
||||||
|
style: theme.iconButtonTheme.style?.copyWith(
|
||||||
|
shape: MaterialStatePropertyAll(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: borderRadius,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onPressed: showSheet,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
import 'package:spotube/collections/spotube_icons.dart';
|
import 'package:spotube/collections/spotube_icons.dart';
|
||||||
import 'package:spotube/components/library/user_local_tracks.dart';
|
import 'package:spotube/components/library/user_local_tracks.dart';
|
||||||
|
import 'package:spotube/components/shared/adaptive/adaptive_pop_sheet_list.dart';
|
||||||
import 'package:spotube/extensions/context.dart';
|
import 'package:spotube/extensions/context.dart';
|
||||||
|
|
||||||
class SortTracksDropdown extends StatelessWidget {
|
class SortTracksDropdown extends StatelessWidget {
|
||||||
@ -15,44 +16,62 @@ class SortTracksDropdown extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return PopupMenuButton<SortBy>(
|
return ListTileTheme(
|
||||||
itemBuilder: (context) {
|
shape: RoundedRectangleBorder(
|
||||||
return [
|
borderRadius: BorderRadius.circular(8),
|
||||||
PopupMenuItem(
|
),
|
||||||
|
child: AdaptivePopSheetList<SortBy>(
|
||||||
|
children: [
|
||||||
|
PopSheetEntry(
|
||||||
value: SortBy.none,
|
value: SortBy.none,
|
||||||
enabled: value != SortBy.none,
|
enabled: value != SortBy.none,
|
||||||
child: Text(context.l10n.none),
|
child: Text(context.l10n.none),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopSheetEntry(
|
||||||
value: SortBy.ascending,
|
value: SortBy.ascending,
|
||||||
enabled: value != SortBy.ascending,
|
enabled: value != SortBy.ascending,
|
||||||
child: Text(context.l10n.sort_a_z),
|
child: Text(context.l10n.sort_a_z),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopSheetEntry(
|
||||||
value: SortBy.descending,
|
value: SortBy.descending,
|
||||||
enabled: value != SortBy.descending,
|
enabled: value != SortBy.descending,
|
||||||
child: Text(context.l10n.sort_z_a),
|
child: Text(context.l10n.sort_z_a),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopSheetEntry(
|
||||||
value: SortBy.dateAdded,
|
value: SortBy.dateAdded,
|
||||||
enabled: value != SortBy.dateAdded,
|
enabled: value != SortBy.dateAdded,
|
||||||
child: Text(context.l10n.sort_date),
|
child: Text(context.l10n.sort_date),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopSheetEntry(
|
||||||
value: SortBy.artist,
|
value: SortBy.artist,
|
||||||
enabled: value != SortBy.artist,
|
enabled: value != SortBy.artist,
|
||||||
child: Text(context.l10n.sort_artist),
|
child: Text(context.l10n.sort_artist),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
PopSheetEntry(
|
||||||
value: SortBy.album,
|
value: SortBy.album,
|
||||||
enabled: value != SortBy.album,
|
enabled: value != SortBy.album,
|
||||||
child: Text(context.l10n.sort_album),
|
child: Text(context.l10n.sort_album),
|
||||||
),
|
),
|
||||||
];
|
],
|
||||||
},
|
headings: [
|
||||||
|
Text(context.l10n.sort_tracks),
|
||||||
|
],
|
||||||
onSelected: onChanged,
|
onSelected: onChanged,
|
||||||
tooltip: context.l10n.sort_tracks,
|
tooltip: context.l10n.sort_tracks,
|
||||||
icon: const Icon(SpotubeIcons.sort),
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
|
||||||
|
child: DefaultTextStyle(
|
||||||
|
style: Theme.of(context).textTheme.titleSmall!,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const Icon(SpotubeIcons.sort),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(context.l10n.sort_tracks),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user