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: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(),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
@ -32,7 +32,11 @@ Future<void> Function(Track track, int index) useTrackTilePlayCallback(
|
||||
ref.read(presentationStateProvider(options.collection).notifier);
|
||||
|
||||
if (state.selectedTracks.isNotEmpty) {
|
||||
notifier.selectTrack(track);
|
||||
if (state.selectedTracks.contains(track)) {
|
||||
notifier.deselectTrack(track);
|
||||
} else {
|
||||
notifier.selectTrack(track);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -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,225 +91,232 @@ 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),
|
||||
selected: isSelected,
|
||||
onTap: () async {
|
||||
try {
|
||||
isLoading.value = true;
|
||||
await onTap?.call();
|
||||
} finally {
|
||||
if (context.mounted) {
|
||||
isLoading.value = false;
|
||||
}
|
||||
builder: (context, isHovering) => ButtonTile(
|
||||
selected: isSelected,
|
||||
onPressed: () async {
|
||||
if (isBlackListed) return;
|
||||
try {
|
||||
isLoading.value = true;
|
||||
await onTap?.call();
|
||||
} finally {
|
||||
if (context.mounted) {
|
||||
isLoading.value = false;
|
||||
}
|
||||
},
|
||||
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,
|
||||
),
|
||||
leading: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
...?leadingActions,
|
||||
AnimatedCrossFade(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
crossFadeState: index != null && onChanged == null
|
||||
? CrossFadeState.showSecond
|
||||
: CrossFadeState.showFirst,
|
||||
firstChild: Checkbox(
|
||||
state: selected
|
||||
? CheckboxState.checked
|
||||
: CheckboxState.unchecked,
|
||||
onChanged: (state) =>
|
||||
onChanged?.call(state == CheckboxState.checked),
|
||||
),
|
||||
secondChild: constrains.smAndDown
|
||||
? const SizedBox(width: 16)
|
||||
: SizedBox(
|
||||
width: 50,
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 6),
|
||||
child: Text(
|
||||
'${(index ?? 0) + 1}',
|
||||
maxLines: 1,
|
||||
style: theme.typography.small,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
}
|
||||
},
|
||||
onLongPress: onLongPress,
|
||||
style: (isBlackListed
|
||||
? ButtonVariance.destructive
|
||||
: ButtonVariance.ghost)
|
||||
.copyWith(
|
||||
padding: (context, states) =>
|
||||
const EdgeInsets.symmetric(vertical: 8, horizontal: 0),
|
||||
),
|
||||
leading: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
...?leadingActions,
|
||||
AnimatedCrossFade(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
crossFadeState: index != null && onChanged == null
|
||||
? CrossFadeState.showSecond
|
||||
: CrossFadeState.showFirst,
|
||||
firstChild: Checkbox(
|
||||
state: selected
|
||||
? CheckboxState.checked
|
||||
: CheckboxState.unchecked,
|
||||
onChanged: (state) =>
|
||||
onChanged?.call(state == CheckboxState.checked),
|
||||
),
|
||||
secondChild: constrains.smAndDown
|
||||
? const SizedBox(width: 16)
|
||||
: SizedBox(
|
||||
width: 50,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||||
child: Text(
|
||||
'${(index ?? 0) + 1}',
|
||||
maxLines: 1,
|
||||
style: theme.typography.small,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
Stack(
|
||||
children: [
|
||||
Container(
|
||||
height: 40,
|
||||
width: 40,
|
||||
),
|
||||
),
|
||||
Stack(
|
||||
children: [
|
||||
Container(
|
||||
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(
|
||||
borderRadius: theme.borderRadiusMd,
|
||||
image: DecorationImage(
|
||||
fit: BoxFit.cover,
|
||||
image: UniversalImage.imageProvider(
|
||||
(track.album?.images).asUrlString(
|
||||
placeholder: ImagePlaceholder.albumArt,
|
||||
color: isHovering
|
||||
? Colors.black.withAlpha(102)
|
||||
: 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(),
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned.fill(
|
||||
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(),
|
||||
onPressed: () {
|
||||
context.pushNamed(
|
||||
TrackPage.name,
|
||||
pathParameters: {
|
||||
"id": track.id!,
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
track.name!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
title: Row(
|
||||
children: [
|
||||
},
|
||||
),
|
||||
if (constrains.mdAndUp) ...[
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 6,
|
||||
flex: 4,
|
||||
child: switch (track) {
|
||||
LocalTrack() => Text(
|
||||
track.name!,
|
||||
track.album!.name!,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
_ => LinkText(
|
||||
track.name!,
|
||||
"/track/${track.id}",
|
||||
push: true,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
},
|
||||
),
|
||||
if (constrains.mdAndUp) ...[
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
flex: 4,
|
||||
child: switch (track) {
|
||||
LocalTrack() => Text(
|
||||
_ => Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: LinkText(
|
||||
track.album!.name!,
|
||||
maxLines: 1,
|
||||
"/album/${track.album?.id}",
|
||||
extra: track.album,
|
||||
push: true,
|
||||
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,
|
||||
child: track is LocalTrack
|
||||
? Text(
|
||||
track.artists?.asString() ?? '',
|
||||
)
|
||||
: ClipRect(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxHeight: 40),
|
||||
child: ArtistLink(
|
||||
artists: track.artists ?? [],
|
||||
onOverflowArtistClick: () => ServiceUtils.pushNamed(
|
||||
context,
|
||||
TrackPage.name,
|
||||
pathParameters: {
|
||||
"id": track.id!,
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
subtitle: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: track is LocalTrack
|
||||
? Text(
|
||||
track.artists?.asString() ?? '',
|
||||
)
|
||||
: ClipRect(
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxHeight: 40),
|
||||
child: ArtistLink(
|
||||
artists: track.artists ?? [],
|
||||
onOverflowArtistClick: () => ServiceUtils.pushNamed(
|
||||
context,
|
||||
TrackPage.name,
|
||||
pathParameters: {
|
||||
"id": track.id!,
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
Duration(milliseconds: track.durationMs ?? 0)
|
||||
.toHumanReadableString(padZero: false),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
TrackOptions(
|
||||
track: track,
|
||||
playlistId: playlistId,
|
||||
userPlaylist: userPlaylist,
|
||||
showMenuCbRef: showOptionCbRef,
|
||||
),
|
||||
if (kIsDesktop) const Gap(10),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
Duration(milliseconds: track.durationMs ?? 0)
|
||||
.toHumanReadableString(padZero: false),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
TrackOptions(
|
||||
track: track,
|
||||
playlistId: playlistId,
|
||||
userPlaylist: userPlaylist,
|
||||
showMenuCbRef: showOptionCbRef,
|
||||
),
|
||||
if (kIsDesktop) const Gap(10),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -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,73 +30,78 @@ class ButtonTile extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final ThemeData(:colorScheme, :typography) = Theme.of(context);
|
||||
|
||||
return Button(
|
||||
enabled: enabled,
|
||||
onPressed: onPressed,
|
||||
style: style.copyWith(
|
||||
padding: padding != null ? (context, states, value) => padding! : null,
|
||||
decoration: (context, states, value) {
|
||||
final decoration = style.decoration(context, states) as BoxDecoration;
|
||||
return GestureDetector(
|
||||
onLongPress: onLongPress,
|
||||
child: Button(
|
||||
enabled: enabled,
|
||||
onPressed: onPressed,
|
||||
style: style.copyWith(
|
||||
padding:
|
||||
padding != null ? (context, states, value) => padding! : null,
|
||||
decoration: (context, states, value) {
|
||||
final decoration =
|
||||
style.decoration(context, states) as BoxDecoration;
|
||||
|
||||
if (selected) {
|
||||
return switch (style) {
|
||||
ButtonVariance.outline => decoration.copyWith(
|
||||
border: Border.all(
|
||||
color: colorScheme.primary,
|
||||
width: 1.0,
|
||||
if (selected) {
|
||||
return switch (style) {
|
||||
ButtonVariance.outline => decoration.copyWith(
|
||||
border: Border.all(
|
||||
color: colorScheme.primary,
|
||||
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;
|
||||
},
|
||||
iconTheme: (context, states, value) {
|
||||
final iconTheme = style.iconTheme(context, states);
|
||||
return decoration;
|
||||
},
|
||||
iconTheme: (context, states, value) {
|
||||
final iconTheme = style.iconTheme(context, states);
|
||||
|
||||
if (selected && style == ButtonVariance.outline) {
|
||||
return iconTheme.copyWith(
|
||||
color: colorScheme.primary,
|
||||
);
|
||||
}
|
||||
if (selected && style == ButtonVariance.outline) {
|
||||
return iconTheme.copyWith(
|
||||
color: colorScheme.primary,
|
||||
);
|
||||
}
|
||||
|
||||
return iconTheme;
|
||||
},
|
||||
textStyle: (context, states, value) {
|
||||
final textStyle = style.textStyle(context, states);
|
||||
return iconTheme;
|
||||
},
|
||||
textStyle: (context, states, value) {
|
||||
final textStyle = style.textStyle(context, states);
|
||||
|
||||
if (selected && style == ButtonVariance.outline) {
|
||||
return textStyle.copyWith(
|
||||
color: colorScheme.primary,
|
||||
);
|
||||
}
|
||||
if (selected && style == ButtonVariance.outline) {
|
||||
return textStyle.copyWith(
|
||||
color: colorScheme.primary,
|
||||
);
|
||||
}
|
||||
|
||||
return textStyle;
|
||||
},
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Basic(
|
||||
padding: EdgeInsets.zero,
|
||||
leadingAlignment: Alignment.center,
|
||||
trailingAlignment: Alignment.center,
|
||||
leading: leading,
|
||||
title: title,
|
||||
subtitle:
|
||||
style == ButtonVariance.outline && selected && subtitle != null
|
||||
? DefaultTextStyle(
|
||||
style: typography.xSmall.copyWith(
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
child: subtitle!,
|
||||
)
|
||||
: subtitle,
|
||||
trailing: trailing,
|
||||
return textStyle;
|
||||
},
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: Basic(
|
||||
padding: EdgeInsets.zero,
|
||||
leadingAlignment: Alignment.center,
|
||||
trailingAlignment: Alignment.center,
|
||||
leading: leading,
|
||||
title: title,
|
||||
subtitle:
|
||||
style == ButtonVariance.outline && selected && subtitle != null
|
||||
? DefaultTextStyle(
|
||||
style: typography.xSmall.copyWith(
|
||||
color: colorScheme.primary,
|
||||
),
|
||||
child: subtitle!,
|
||||
)
|
||||
: subtitle,
|
||||
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(() {
|
||||
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(
|
||||
|
Loading…
Reference in New Issue
Block a user