mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45: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) {
|
||||||
notifier.selectTrack(track);
|
if (state.selectedTracks.contains(track)) {
|
||||||
|
notifier.deselectTrack(track);
|
||||||
|
} else {
|
||||||
|
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,225 +91,232 @@ 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,
|
selected: isSelected,
|
||||||
child: ListTile(
|
onPressed: () async {
|
||||||
selectedColor: theme.colorScheme.primary,
|
if (isBlackListed) return;
|
||||||
selectedTileColor: theme.colorScheme.primary.withOpacity(0.1),
|
try {
|
||||||
selected: isSelected,
|
isLoading.value = true;
|
||||||
onTap: () async {
|
await onTap?.call();
|
||||||
try {
|
} finally {
|
||||||
isLoading.value = true;
|
if (context.mounted) {
|
||||||
await onTap?.call();
|
isLoading.value = false;
|
||||||
} finally {
|
|
||||||
if (context.mounted) {
|
|
||||||
isLoading.value = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
onLongPress: onLongPress,
|
},
|
||||||
enabled: !isBlackListed,
|
onLongPress: onLongPress,
|
||||||
contentPadding: EdgeInsets.zero,
|
style: (isBlackListed
|
||||||
tileColor: isBlackListed ? theme.colorScheme.destructive : null,
|
? ButtonVariance.destructive
|
||||||
horizontalTitleGap: 12,
|
: ButtonVariance.ghost)
|
||||||
leadingAndTrailingTextStyle: theme.typography.normal.copyWith(
|
.copyWith(
|
||||||
color: theme.colorScheme.foreground,
|
padding: (context, states) =>
|
||||||
),
|
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
|
||||||
titleTextStyle: theme.typography.normal.copyWith(
|
),
|
||||||
color: theme.colorScheme.foreground,
|
leading: Row(
|
||||||
),
|
mainAxisSize: MainAxisSize.min,
|
||||||
subtitleTextStyle: theme.typography.xSmall.copyWith(
|
children: [
|
||||||
color: theme.colorScheme.mutedForeground,
|
...?leadingActions,
|
||||||
),
|
AnimatedCrossFade(
|
||||||
leading: Row(
|
duration: const Duration(milliseconds: 300),
|
||||||
mainAxisSize: MainAxisSize.min,
|
crossFadeState: index != null && onChanged == null
|
||||||
children: [
|
? CrossFadeState.showSecond
|
||||||
...?leadingActions,
|
: CrossFadeState.showFirst,
|
||||||
AnimatedCrossFade(
|
firstChild: Checkbox(
|
||||||
duration: const Duration(milliseconds: 300),
|
state: selected
|
||||||
crossFadeState: index != null && onChanged == null
|
? CheckboxState.checked
|
||||||
? CrossFadeState.showSecond
|
: CheckboxState.unchecked,
|
||||||
: CrossFadeState.showFirst,
|
onChanged: (state) =>
|
||||||
firstChild: Checkbox(
|
onChanged?.call(state == CheckboxState.checked),
|
||||||
state: selected
|
),
|
||||||
? CheckboxState.checked
|
secondChild: constrains.smAndDown
|
||||||
: CheckboxState.unchecked,
|
? const SizedBox(width: 16)
|
||||||
onChanged: (state) =>
|
: SizedBox(
|
||||||
onChanged?.call(state == CheckboxState.checked),
|
width: 50,
|
||||||
),
|
child: Padding(
|
||||||
secondChild: constrains.smAndDown
|
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||||
? const SizedBox(width: 16)
|
child: Text(
|
||||||
: SizedBox(
|
'${(index ?? 0) + 1}',
|
||||||
width: 50,
|
maxLines: 1,
|
||||||
child: Padding(
|
style: theme.typography.small,
|
||||||
padding:
|
textAlign: TextAlign.center,
|
||||||
const EdgeInsets.symmetric(horizontal: 6),
|
|
||||||
child: Text(
|
|
||||||
'${(index ?? 0) + 1}',
|
|
||||||
maxLines: 1,
|
|
||||||
style: theme.typography.small,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Stack(
|
),
|
||||||
children: [
|
Stack(
|
||||||
Container(
|
children: [
|
||||||
height: 40,
|
Container(
|
||||||
width: 40,
|
height: 40,
|
||||||
|
width: 40,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: theme.borderRadiusMd,
|
||||||
|
image: DecorationImage(
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
image: UniversalImage.imageProvider(
|
||||||
|
(track.album?.images).asUrlString(
|
||||||
|
placeholder: ImagePlaceholder.albumArt,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: theme.borderRadiusMd,
|
borderRadius: theme.borderRadiusMd,
|
||||||
image: DecorationImage(
|
color: isHovering
|
||||||
fit: BoxFit.cover,
|
? Colors.black.withAlpha(102)
|
||||||
image: UniversalImage.imageProvider(
|
: Colors.transparent,
|
||||||
(track.album?.images).asUrlString(
|
),
|
||||||
placeholder: ImagePlaceholder.albumArt,
|
),
|
||||||
|
),
|
||||||
|
Positioned.fill(
|
||||||
|
child: Center(
|
||||||
|
child: Skeleton.ignore(
|
||||||
|
child: Consumer(
|
||||||
|
builder: (context, ref, _) {
|
||||||
|
final isFetchingActiveTrack =
|
||||||
|
ref.watch(queryingTrackInfoProvider);
|
||||||
|
return AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
child: switch ((
|
||||||
|
isPlaying,
|
||||||
|
isFetchingActiveTrack,
|
||||||
|
isPlaying,
|
||||||
|
isHovering,
|
||||||
|
isLoading.value
|
||||||
|
)) {
|
||||||
|
(true, true, _, _, _) ||
|
||||||
|
(_, _, _, _, true) =>
|
||||||
|
const SizedBox(
|
||||||
|
width: 26,
|
||||||
|
height: 26,
|
||||||
|
child:
|
||||||
|
CircularProgressIndicator(size: 1.5),
|
||||||
|
),
|
||||||
|
(_, _, true, _, _) => Icon(
|
||||||
|
SpotubeIcons.pause,
|
||||||
|
color: theme.colorScheme.primary,
|
||||||
|
),
|
||||||
|
(_, _, _, true, _) => const Icon(
|
||||||
|
SpotubeIcons.play,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
_ => const SizedBox.shrink(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 6,
|
||||||
|
child: switch (track) {
|
||||||
|
LocalTrack() => Text(
|
||||||
|
track.name!,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
_ => Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Button(
|
||||||
|
style: ButtonVariance.link.copyWith(
|
||||||
|
padding: (context, states) => EdgeInsets.zero,
|
||||||
),
|
),
|
||||||
),
|
onPressed: () {
|
||||||
),
|
context.pushNamed(
|
||||||
),
|
TrackPage.name,
|
||||||
),
|
pathParameters: {
|
||||||
Positioned.fill(
|
"id": track.id!,
|
||||||
child: AnimatedContainer(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: theme.borderRadiusMd,
|
|
||||||
color: isHovering
|
|
||||||
? Colors.black.withOpacity(0.4)
|
|
||||||
: Colors.transparent,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned.fill(
|
|
||||||
child: Center(
|
|
||||||
child: Skeleton.ignore(
|
|
||||||
child: Consumer(
|
|
||||||
builder: (context, ref, _) {
|
|
||||||
final isFetchingActiveTrack =
|
|
||||||
ref.watch(queryingTrackInfoProvider);
|
|
||||||
return AnimatedSwitcher(
|
|
||||||
duration: const Duration(milliseconds: 300),
|
|
||||||
child: switch ((
|
|
||||||
isPlaying,
|
|
||||||
isFetchingActiveTrack,
|
|
||||||
isPlaying,
|
|
||||||
isHovering,
|
|
||||||
isLoading.value
|
|
||||||
)) {
|
|
||||||
(true, true, _, _, _) ||
|
|
||||||
(_, _, _, _, true) =>
|
|
||||||
const SizedBox(
|
|
||||||
width: 26,
|
|
||||||
height: 26,
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
size: 1.5),
|
|
||||||
),
|
|
||||||
(_, _, true, _, _) => Icon(
|
|
||||||
SpotubeIcons.pause,
|
|
||||||
color: theme.colorScheme.primary,
|
|
||||||
),
|
|
||||||
(_, _, _, true, _) => const Icon(
|
|
||||||
SpotubeIcons.play,
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
_ => const SizedBox.shrink(),
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
child: Text(
|
||||||
|
track.name!,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
],
|
},
|
||||||
),
|
),
|
||||||
],
|
if (constrains.mdAndUp) ...[
|
||||||
),
|
const SizedBox(width: 8),
|
||||||
title: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 6,
|
flex: 4,
|
||||||
child: switch (track) {
|
child: switch (track) {
|
||||||
LocalTrack() => Text(
|
LocalTrack() => Text(
|
||||||
track.name!,
|
track.album!.name!,
|
||||||
maxLines: 1,
|
maxLines: 1,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
_ => LinkText(
|
_ => Align(
|
||||||
track.name!,
|
alignment: Alignment.centerLeft,
|
||||||
"/track/${track.id}",
|
child: LinkText(
|
||||||
push: true,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
),
|
|
||||||
if (constrains.mdAndUp) ...[
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
Expanded(
|
|
||||||
flex: 4,
|
|
||||||
child: switch (track) {
|
|
||||||
LocalTrack() => Text(
|
|
||||||
track.album!.name!,
|
track.album!.name!,
|
||||||
maxLines: 1,
|
"/album/${track.album?.id}",
|
||||||
|
extra: track.album,
|
||||||
|
push: true,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
_ => Align(
|
)
|
||||||
alignment: Alignment.centerLeft,
|
},
|
||||||
child: LinkText(
|
),
|
||||||
track.album!.name!,
|
|
||||||
"/album/${track.album?.id}",
|
|
||||||
extra: track.album,
|
|
||||||
push: true,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
),
|
],
|
||||||
subtitle: Align(
|
),
|
||||||
alignment: Alignment.centerLeft,
|
subtitle: Align(
|
||||||
child: track is LocalTrack
|
alignment: Alignment.centerLeft,
|
||||||
? Text(
|
child: track is LocalTrack
|
||||||
track.artists?.asString() ?? '',
|
? Text(
|
||||||
)
|
track.artists?.asString() ?? '',
|
||||||
: ClipRect(
|
)
|
||||||
child: ConstrainedBox(
|
: ClipRect(
|
||||||
constraints: const BoxConstraints(maxHeight: 40),
|
child: ConstrainedBox(
|
||||||
child: ArtistLink(
|
constraints: const BoxConstraints(maxHeight: 40),
|
||||||
artists: track.artists ?? [],
|
child: ArtistLink(
|
||||||
onOverflowArtistClick: () => ServiceUtils.pushNamed(
|
artists: track.artists ?? [],
|
||||||
context,
|
onOverflowArtistClick: () => ServiceUtils.pushNamed(
|
||||||
TrackPage.name,
|
context,
|
||||||
pathParameters: {
|
TrackPage.name,
|
||||||
"id": track.id!,
|
pathParameters: {
|
||||||
},
|
"id": track.id!,
|
||||||
),
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
trailing: Row(
|
),
|
||||||
mainAxisSize: MainAxisSize.min,
|
trailing: Row(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
const SizedBox(width: 8),
|
children: [
|
||||||
Text(
|
const SizedBox(width: 8),
|
||||||
Duration(milliseconds: track.durationMs ?? 0)
|
Text(
|
||||||
.toHumanReadableString(padZero: false),
|
Duration(milliseconds: track.durationMs ?? 0)
|
||||||
maxLines: 1,
|
.toHumanReadableString(padZero: false),
|
||||||
overflow: TextOverflow.ellipsis,
|
maxLines: 1,
|
||||||
),
|
overflow: TextOverflow.ellipsis,
|
||||||
TrackOptions(
|
),
|
||||||
track: track,
|
TrackOptions(
|
||||||
playlistId: playlistId,
|
track: track,
|
||||||
userPlaylist: userPlaylist,
|
playlistId: playlistId,
|
||||||
showMenuCbRef: showOptionCbRef,
|
userPlaylist: userPlaylist,
|
||||||
),
|
showMenuCbRef: showOptionCbRef,
|
||||||
if (kIsDesktop) const Gap(10),
|
),
|
||||||
],
|
if (kIsDesktop) const Gap(10),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -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,73 +30,78 @@ 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(
|
||||||
enabled: enabled,
|
onLongPress: onLongPress,
|
||||||
onPressed: onPressed,
|
child: Button(
|
||||||
style: style.copyWith(
|
enabled: enabled,
|
||||||
padding: padding != null ? (context, states, value) => padding! : null,
|
onPressed: onPressed,
|
||||||
decoration: (context, states, value) {
|
style: style.copyWith(
|
||||||
final decoration = style.decoration(context, states) as BoxDecoration;
|
padding:
|
||||||
|
padding != null ? (context, states, value) => padding! : null,
|
||||||
|
decoration: (context, states, value) {
|
||||||
|
final decoration =
|
||||||
|
style.decoration(context, states) as BoxDecoration;
|
||||||
|
|
||||||
if (selected) {
|
if (selected) {
|
||||||
return switch (style) {
|
return switch (style) {
|
||||||
ButtonVariance.outline => decoration.copyWith(
|
ButtonVariance.outline => decoration.copyWith(
|
||||||
border: Border.all(
|
border: Border.all(
|
||||||
color: colorScheme.primary,
|
color: colorScheme.primary,
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
|
),
|
||||||
|
color: colorScheme.primary.withAlpha(25),
|
||||||
),
|
),
|
||||||
color: colorScheme.primary.withAlpha(25),
|
ButtonVariance.ghost || _ => decoration.copyWith(
|
||||||
),
|
color: colorScheme.primary.withAlpha(25),
|
||||||
ButtonVariance.ghost || _ => decoration.copyWith(
|
),
|
||||||
color: colorScheme.primary.withAlpha(25),
|
};
|
||||||
),
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return decoration;
|
return decoration;
|
||||||
},
|
},
|
||||||
iconTheme: (context, states, value) {
|
iconTheme: (context, states, value) {
|
||||||
final iconTheme = style.iconTheme(context, states);
|
final iconTheme = style.iconTheme(context, states);
|
||||||
|
|
||||||
if (selected && style == ButtonVariance.outline) {
|
if (selected && style == ButtonVariance.outline) {
|
||||||
return iconTheme.copyWith(
|
return iconTheme.copyWith(
|
||||||
color: colorScheme.primary,
|
color: colorScheme.primary,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return iconTheme;
|
return iconTheme;
|
||||||
},
|
},
|
||||||
textStyle: (context, states, value) {
|
textStyle: (context, states, value) {
|
||||||
final textStyle = style.textStyle(context, states);
|
final textStyle = style.textStyle(context, states);
|
||||||
|
|
||||||
if (selected && style == ButtonVariance.outline) {
|
if (selected && style == ButtonVariance.outline) {
|
||||||
return textStyle.copyWith(
|
return textStyle.copyWith(
|
||||||
color: colorScheme.primary,
|
color: colorScheme.primary,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return textStyle;
|
return textStyle;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
alignment: Alignment.centerLeft,
|
alignment: Alignment.centerLeft,
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
child: Basic(
|
child: Basic(
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
leadingAlignment: Alignment.center,
|
leadingAlignment: Alignment.center,
|
||||||
trailingAlignment: Alignment.center,
|
trailingAlignment: Alignment.center,
|
||||||
leading: leading,
|
leading: leading,
|
||||||
title: title,
|
title: title,
|
||||||
subtitle:
|
subtitle:
|
||||||
style == ButtonVariance.outline && selected && subtitle != null
|
style == ButtonVariance.outline && selected && subtitle != null
|
||||||
? DefaultTextStyle(
|
? DefaultTextStyle(
|
||||||
style: typography.xSmall.copyWith(
|
style: typography.xSmall.copyWith(
|
||||||
color: colorScheme.primary,
|
color: colorScheme.primary,
|
||||||
),
|
),
|
||||||
child: subtitle!,
|
child: subtitle!,
|
||||||
)
|
)
|
||||||
: subtitle,
|
: subtitle,
|
||||||
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