refactor: use shadcn for TrackTile

This commit is contained in:
Kingkor Roy Tirtho 2025-01-08 22:16:46 +06:00
parent e54a646073
commit 88906098dd
6 changed files with 309 additions and 268 deletions

View File

@ -1,4 +1,3 @@
import 'package:flutter/material.dart' show ListTile;
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
@ -21,9 +20,6 @@ class TrackPresentation extends HookConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final headerTextStyle = context.theme.typography.small.copyWith(
color: context.theme.colorScheme.mutedForeground,
);
final scrollController = useScrollController();
final focusNode = useFocusNode();
final scale = context.theme.scaling;
@ -66,10 +62,11 @@ class TrackPresentation extends HookConsumerWidget {
TrackPresentationModifiersSection(
focusNode: focusNode,
),
ListTile(
titleTextStyle: headerTextStyle,
subtitleTextStyle: headerTextStyle,
leadingAndTrailingTextStyle: headerTextStyle,
Basic(
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 16,
),
leading: constrains.mdAndUp ? const Text(" #") : null,
title: Row(
children: [
@ -85,7 +82,7 @@ class TrackPresentation extends HookConsumerWidget {
Text(context.l10n.duration),
],
),
),
).small().muted(),
],
);
},

View File

@ -32,7 +32,11 @@ Future<void> Function(Track track, int index) useTrackTilePlayCallback(
ref.read(presentationStateProvider(options.collection).notifier);
if (state.selectedTracks.isNotEmpty) {
if (state.selectedTracks.contains(track)) {
notifier.deselectTrack(track);
} else {
notifier.selectTrack(track);
}
return;
}

View File

@ -1,8 +1,8 @@
import 'dart:async';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart' show ListTile, Material, MaterialType;
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/link_text.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/button_variance.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/duration.dart';
import 'package:spotube/extensions/image.dart';
@ -89,13 +91,10 @@ class TrackTile extends HookConsumerWidget {
},
child: HoverBuilder(
permanentState: isSelected || constrains.smAndDown ? true : null,
builder: (context, isHovering) => Material(
type: MaterialType.transparency,
child: ListTile(
selectedColor: theme.colorScheme.primary,
selectedTileColor: theme.colorScheme.primary.withOpacity(0.1),
builder: (context, isHovering) => ButtonTile(
selected: isSelected,
onTap: () async {
onPressed: () async {
if (isBlackListed) return;
try {
isLoading.value = true;
await onTap?.call();
@ -106,18 +105,12 @@ class TrackTile extends HookConsumerWidget {
}
},
onLongPress: onLongPress,
enabled: !isBlackListed,
contentPadding: EdgeInsets.zero,
tileColor: isBlackListed ? theme.colorScheme.destructive : null,
horizontalTitleGap: 12,
leadingAndTrailingTextStyle: theme.typography.normal.copyWith(
color: theme.colorScheme.foreground,
),
titleTextStyle: theme.typography.normal.copyWith(
color: theme.colorScheme.foreground,
),
subtitleTextStyle: theme.typography.xSmall.copyWith(
color: theme.colorScheme.mutedForeground,
style: (isBlackListed
? ButtonVariance.destructive
: ButtonVariance.ghost)
.copyWith(
padding: (context, states) =>
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
),
leading: Row(
mainAxisSize: MainAxisSize.min,
@ -140,8 +133,7 @@ class TrackTile extends HookConsumerWidget {
: SizedBox(
width: 50,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 6),
padding: const EdgeInsets.symmetric(horizontal: 6),
child: Text(
'${(index ?? 0) + 1}',
maxLines: 1,
@ -174,7 +166,7 @@ class TrackTile extends HookConsumerWidget {
decoration: BoxDecoration(
borderRadius: theme.borderRadiusMd,
color: isHovering
? Colors.black.withOpacity(0.4)
? Colors.black.withAlpha(102)
: Colors.transparent,
),
),
@ -200,8 +192,8 @@ class TrackTile extends HookConsumerWidget {
const SizedBox(
width: 26,
height: 26,
child: CircularProgressIndicator(
size: 1.5),
child:
CircularProgressIndicator(size: 1.5),
),
(_, _, true, _, _) => Icon(
SpotubeIcons.pause,
@ -233,13 +225,31 @@ class TrackTile extends HookConsumerWidget {
maxLines: 1,
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/${track.id}",
push: true,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
),
],
),
},
),
if (constrains.mdAndUp) ...[
@ -310,7 +320,6 @@ class TrackTile extends HookConsumerWidget {
),
),
),
),
);
});
}

View File

@ -6,7 +6,8 @@ class ButtonTile extends StatelessWidget {
final Widget? leading;
final Widget? trailing;
final bool enabled;
final void Function()? onPressed;
final VoidCallback? onPressed;
final VoidCallback? onLongPress;
final bool selected;
final ButtonVariance style;
final EdgeInsets? padding;
@ -19,6 +20,7 @@ class ButtonTile extends StatelessWidget {
this.trailing,
this.enabled = true,
this.onPressed,
this.onLongPress,
this.selected = false,
this.padding,
this.style = ButtonVariance.outline,
@ -28,13 +30,17 @@ class ButtonTile extends StatelessWidget {
Widget build(BuildContext context) {
final ThemeData(:colorScheme, :typography) = Theme.of(context);
return Button(
return GestureDetector(
onLongPress: onLongPress,
child: Button(
enabled: enabled,
onPressed: onPressed,
style: style.copyWith(
padding: padding != null ? (context, states, value) => padding! : null,
padding:
padding != null ? (context, states, value) => padding! : null,
decoration: (context, states, value) {
final decoration = style.decoration(context, states) as BoxDecoration;
final decoration =
style.decoration(context, states) as BoxDecoration;
if (selected) {
return switch (style) {
@ -97,6 +103,7 @@ class ButtonTile extends StatelessWidget {
trailing: trailing,
),
),
),
);
}
}

View 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,
);
}
}

View File

@ -43,7 +43,7 @@ class HomeGenresSection extends HookConsumerWidget {
useEffect(() {
int times = 0;
Timer.periodic(
final timer = Timer.periodic(
const Duration(seconds: 5),
(timer) {
if (times > 5 || interactedRef.value) {
@ -57,7 +57,10 @@ class HomeGenresSection extends HookConsumerWidget {
},
);
return controller.dispose;
return () {
timer.cancel();
controller.dispose();
};
}, []);
return SliverList.list(