mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
refactor: use shadcn for TrackTile
This commit is contained in:
parent
e54a646073
commit
88906098dd
@ -1,4 +1,3 @@
|
|||||||
import 'package:flutter/material.dart' show ListTile;
|
|
||||||
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:shadcn_flutter/shadcn_flutter.dart';
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
@ -21,9 +20,6 @@ class TrackPresentation extends HookConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, ref) {
|
Widget build(BuildContext context, ref) {
|
||||||
final headerTextStyle = context.theme.typography.small.copyWith(
|
|
||||||
color: context.theme.colorScheme.mutedForeground,
|
|
||||||
);
|
|
||||||
final scrollController = useScrollController();
|
final scrollController = useScrollController();
|
||||||
final focusNode = useFocusNode();
|
final focusNode = useFocusNode();
|
||||||
final scale = context.theme.scaling;
|
final scale = context.theme.scaling;
|
||||||
@ -66,10 +62,11 @@ class TrackPresentation extends HookConsumerWidget {
|
|||||||
TrackPresentationModifiersSection(
|
TrackPresentationModifiersSection(
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
),
|
),
|
||||||
ListTile(
|
Basic(
|
||||||
titleTextStyle: headerTextStyle,
|
padding: const EdgeInsets.symmetric(
|
||||||
subtitleTextStyle: headerTextStyle,
|
vertical: 8,
|
||||||
leadingAndTrailingTextStyle: headerTextStyle,
|
horizontal: 16,
|
||||||
|
),
|
||||||
leading: constrains.mdAndUp ? const Text(" #") : null,
|
leading: constrains.mdAndUp ? const Text(" #") : null,
|
||||||
title: Row(
|
title: Row(
|
||||||
children: [
|
children: [
|
||||||
@ -85,7 +82,7 @@ class TrackPresentation extends HookConsumerWidget {
|
|||||||
Text(context.l10n.duration),
|
Text(context.l10n.duration),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
).small().muted(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -32,7 +32,11 @@ Future<void> Function(Track track, int index) useTrackTilePlayCallback(
|
|||||||
ref.read(presentationStateProvider(options.collection).notifier);
|
ref.read(presentationStateProvider(options.collection).notifier);
|
||||||
|
|
||||||
if (state.selectedTracks.isNotEmpty) {
|
if (state.selectedTracks.isNotEmpty) {
|
||||||
|
if (state.selectedTracks.contains(track)) {
|
||||||
|
notifier.deselectTrack(track);
|
||||||
|
} else {
|
||||||
notifier.selectTrack(track);
|
notifier.selectTrack(track);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart' show ListTile, Material, MaterialType;
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
@ -14,7 +14,9 @@ import 'package:spotube/components/image/universal_image.dart';
|
|||||||
import 'package:spotube/components/links/artist_link.dart';
|
import 'package:spotube/components/links/artist_link.dart';
|
||||||
import 'package:spotube/components/links/link_text.dart';
|
import 'package:spotube/components/links/link_text.dart';
|
||||||
import 'package:spotube/components/track_tile/track_options.dart';
|
import 'package:spotube/components/track_tile/track_options.dart';
|
||||||
|
import 'package:spotube/components/ui/button_tile.dart';
|
||||||
import 'package:spotube/extensions/artist_simple.dart';
|
import 'package:spotube/extensions/artist_simple.dart';
|
||||||
|
import 'package:spotube/extensions/button_variance.dart';
|
||||||
import 'package:spotube/extensions/constrains.dart';
|
import 'package:spotube/extensions/constrains.dart';
|
||||||
import 'package:spotube/extensions/duration.dart';
|
import 'package:spotube/extensions/duration.dart';
|
||||||
import 'package:spotube/extensions/image.dart';
|
import 'package:spotube/extensions/image.dart';
|
||||||
@ -89,13 +91,10 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
child: HoverBuilder(
|
child: HoverBuilder(
|
||||||
permanentState: isSelected || constrains.smAndDown ? true : null,
|
permanentState: isSelected || constrains.smAndDown ? true : null,
|
||||||
builder: (context, isHovering) => Material(
|
builder: (context, isHovering) => ButtonTile(
|
||||||
type: MaterialType.transparency,
|
|
||||||
child: ListTile(
|
|
||||||
selectedColor: theme.colorScheme.primary,
|
|
||||||
selectedTileColor: theme.colorScheme.primary.withOpacity(0.1),
|
|
||||||
selected: isSelected,
|
selected: isSelected,
|
||||||
onTap: () async {
|
onPressed: () async {
|
||||||
|
if (isBlackListed) return;
|
||||||
try {
|
try {
|
||||||
isLoading.value = true;
|
isLoading.value = true;
|
||||||
await onTap?.call();
|
await onTap?.call();
|
||||||
@ -106,18 +105,12 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: onLongPress,
|
onLongPress: onLongPress,
|
||||||
enabled: !isBlackListed,
|
style: (isBlackListed
|
||||||
contentPadding: EdgeInsets.zero,
|
? ButtonVariance.destructive
|
||||||
tileColor: isBlackListed ? theme.colorScheme.destructive : null,
|
: ButtonVariance.ghost)
|
||||||
horizontalTitleGap: 12,
|
.copyWith(
|
||||||
leadingAndTrailingTextStyle: theme.typography.normal.copyWith(
|
padding: (context, states) =>
|
||||||
color: theme.colorScheme.foreground,
|
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
|
||||||
),
|
|
||||||
titleTextStyle: theme.typography.normal.copyWith(
|
|
||||||
color: theme.colorScheme.foreground,
|
|
||||||
),
|
|
||||||
subtitleTextStyle: theme.typography.xSmall.copyWith(
|
|
||||||
color: theme.colorScheme.mutedForeground,
|
|
||||||
),
|
),
|
||||||
leading: Row(
|
leading: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@ -140,8 +133,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
: SizedBox(
|
: SizedBox(
|
||||||
width: 50,
|
width: 50,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||||
const EdgeInsets.symmetric(horizontal: 6),
|
|
||||||
child: Text(
|
child: Text(
|
||||||
'${(index ?? 0) + 1}',
|
'${(index ?? 0) + 1}',
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
@ -174,7 +166,7 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: theme.borderRadiusMd,
|
borderRadius: theme.borderRadiusMd,
|
||||||
color: isHovering
|
color: isHovering
|
||||||
? Colors.black.withOpacity(0.4)
|
? Colors.black.withAlpha(102)
|
||||||
: Colors.transparent,
|
: Colors.transparent,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -200,8 +192,8 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 26,
|
width: 26,
|
||||||
height: 26,
|
height: 26,
|
||||||
child: CircularProgressIndicator(
|
child:
|
||||||
size: 1.5),
|
CircularProgressIndicator(size: 1.5),
|
||||||
),
|
),
|
||||||
(_, _, true, _, _) => Icon(
|
(_, _, true, _, _) => Icon(
|
||||||
SpotubeIcons.pause,
|
SpotubeIcons.pause,
|
||||||
@ -233,13 +225,31 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
_ => LinkText(
|
_ => Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Button(
|
||||||
|
style: ButtonVariance.link.copyWith(
|
||||||
|
padding: (context, states) => EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
context.pushNamed(
|
||||||
|
TrackPage.name,
|
||||||
|
pathParameters: {
|
||||||
|
"id": track.id!,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
track.name!,
|
track.name!,
|
||||||
"/track/${track.id}",
|
|
||||||
push: true,
|
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (constrains.mdAndUp) ...[
|
if (constrains.mdAndUp) ...[
|
||||||
@ -310,7 +320,6 @@ class TrackTile extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@ class ButtonTile extends StatelessWidget {
|
|||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
final Widget? trailing;
|
final Widget? trailing;
|
||||||
final bool enabled;
|
final bool enabled;
|
||||||
final void Function()? onPressed;
|
final VoidCallback? onPressed;
|
||||||
|
final VoidCallback? onLongPress;
|
||||||
final bool selected;
|
final bool selected;
|
||||||
final ButtonVariance style;
|
final ButtonVariance style;
|
||||||
final EdgeInsets? padding;
|
final EdgeInsets? padding;
|
||||||
@ -19,6 +20,7 @@ class ButtonTile extends StatelessWidget {
|
|||||||
this.trailing,
|
this.trailing,
|
||||||
this.enabled = true,
|
this.enabled = true,
|
||||||
this.onPressed,
|
this.onPressed,
|
||||||
|
this.onLongPress,
|
||||||
this.selected = false,
|
this.selected = false,
|
||||||
this.padding,
|
this.padding,
|
||||||
this.style = ButtonVariance.outline,
|
this.style = ButtonVariance.outline,
|
||||||
@ -28,13 +30,17 @@ class ButtonTile extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData(:colorScheme, :typography) = Theme.of(context);
|
final ThemeData(:colorScheme, :typography) = Theme.of(context);
|
||||||
|
|
||||||
return Button(
|
return GestureDetector(
|
||||||
|
onLongPress: onLongPress,
|
||||||
|
child: Button(
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
style: style.copyWith(
|
style: style.copyWith(
|
||||||
padding: padding != null ? (context, states, value) => padding! : null,
|
padding:
|
||||||
|
padding != null ? (context, states, value) => padding! : null,
|
||||||
decoration: (context, states, value) {
|
decoration: (context, states, value) {
|
||||||
final decoration = style.decoration(context, states) as BoxDecoration;
|
final decoration =
|
||||||
|
style.decoration(context, states) as BoxDecoration;
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
return switch (style) {
|
return switch (style) {
|
||||||
@ -97,6 +103,7 @@ class ButtonTile extends StatelessWidget {
|
|||||||
trailing: trailing,
|
trailing: trailing,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
21
lib/extensions/button_variance.dart
Normal file
21
lib/extensions/button_variance.dart
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||||
|
|
||||||
|
extension CopyWithButtonVarianceExtension on ButtonVariance {
|
||||||
|
ButtonVariance copyWith({
|
||||||
|
ButtonStateProperty<EdgeInsets>? padding,
|
||||||
|
ButtonStateProperty<Decoration>? decoration,
|
||||||
|
ButtonStateProperty<MouseCursor>? mouseCursor,
|
||||||
|
ButtonStateProperty<IconThemeData>? iconTheme,
|
||||||
|
ButtonStateProperty<EdgeInsets>? margin,
|
||||||
|
ButtonStateProperty<TextStyle>? textStyle,
|
||||||
|
}) {
|
||||||
|
return ButtonVariance(
|
||||||
|
padding: padding ?? this.padding,
|
||||||
|
decoration: decoration ?? this.decoration,
|
||||||
|
mouseCursor: mouseCursor ?? this.mouseCursor,
|
||||||
|
iconTheme: iconTheme ?? this.iconTheme,
|
||||||
|
margin: margin ?? this.margin,
|
||||||
|
textStyle: textStyle ?? this.textStyle,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -43,7 +43,7 @@ class HomeGenresSection extends HookConsumerWidget {
|
|||||||
|
|
||||||
useEffect(() {
|
useEffect(() {
|
||||||
int times = 0;
|
int times = 0;
|
||||||
Timer.periodic(
|
final timer = Timer.periodic(
|
||||||
const Duration(seconds: 5),
|
const Duration(seconds: 5),
|
||||||
(timer) {
|
(timer) {
|
||||||
if (times > 5 || interactedRef.value) {
|
if (times > 5 || interactedRef.value) {
|
||||||
@ -57,7 +57,10 @@ class HomeGenresSection extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return controller.dispose;
|
return () {
|
||||||
|
timer.cancel();
|
||||||
|
controller.dispose();
|
||||||
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return SliverList.list(
|
return SliverList.list(
|
||||||
|
Loading…
Reference in New Issue
Block a user