mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-20 22:41:38 +02:00
feat(ui): unify content cards and preserve press behavior
This commit is contained in:
1 parent
8ff1490d9e
commit
33dca89f28
15 files changed
+182
-129
No files matched your search
@@ -7,6 +7,7 @@ import 'package:spotiflac_android/screens/track_history_snapshot.dart';
|
||||
import 'package:spotiflac_android/theme/app_tokens.dart';
|
||||
import 'package:spotiflac_android/theme/mornye_theme.dart';
|
||||
import 'package:spotiflac_android/widgets/app_switch.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/app_choice_chip.dart';
|
||||
import 'package:spotiflac_android/widgets/app_loading_indicator.dart';
|
||||
import 'package:spotiflac_android/widgets/app_progress_indicator.dart';
|
||||
|
||||
@@ -65,8 +65,9 @@ extension _HomeTabSearchResultsUI on _HomeTabState {
|
||||
}
|
||||
|
||||
if (isUrlNotRecognized) {
|
||||
return Card(
|
||||
return AppContentCard(
|
||||
elevation: 0,
|
||||
mornyeSurface: false,
|
||||
color: colorScheme.errorContainer.withValues(alpha: 0.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: context.tokens.borderRadiusCard,
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:spotiflac_android/widgets/animation_utils.dart'
|
||||
show SkeletonBox;
|
||||
import 'package:spotiflac_android/widgets/app_alert_dialog.dart';
|
||||
import 'package:spotiflac_android/widgets/app_choice_chip.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/app_icon_button.dart';
|
||||
import 'package:spotiflac_android/widgets/app_refresh_indicator.dart';
|
||||
import 'package:spotiflac_android/widgets/app_shape.dart';
|
||||
@@ -225,7 +226,8 @@ class _ListeningStatisticsScreenState
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Card(
|
||||
child: AppContentCard(
|
||||
mornyeSurface: false,
|
||||
color: mornye
|
||||
? null
|
||||
: Theme.of(context).colorScheme.primaryContainer,
|
||||
@@ -333,7 +335,8 @@ class _ListeningStatisticsScreenState
|
||||
SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Card(
|
||||
child: AppContentCard(
|
||||
mornyeSurface: false,
|
||||
// Material leads with the total as an emphasized hero figure.
|
||||
color: mornye ? null : colors.primaryContainer,
|
||||
child: Padding(
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart' show ValueListenable;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:spotiflac_android/widgets/app_alert_dialog.dart';
|
||||
import 'package:spotiflac_android/widgets/app_choice_chip.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/services/local_track_batch_actions.dart';
|
||||
import 'package:spotiflac_android/services/shell_navigation_service.dart';
|
||||
import 'package:spotiflac_android/widgets/error_card.dart';
|
||||
|
||||
@@ -466,49 +466,47 @@ extension _QueueTabCollectionItemWidgets on _QueueTabState {
|
||||
),
|
||||
);
|
||||
|
||||
return Card(
|
||||
return AppContentCard(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 56, height: 56, child: cover),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
mornyeSurface: false,
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(width: 56, height: 56, child: cover),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.titleSmall?.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
subtitle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
subtitle,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
AppIcon(
|
||||
Icons.chevron_right,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
AppIcon(
|
||||
Icons.chevron_right,
|
||||
color: colorScheme.onSurfaceVariant,
|
||||
size: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:intl/intl.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'package:spotiflac_android/services/app_remote_config_service.dart';
|
||||
import 'package:spotiflac_android/utils/adaptive_layout.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/app_icon.dart';
|
||||
import 'package:spotiflac_android/widgets/app_progress_indicator.dart';
|
||||
import 'package:spotiflac_android/widgets/donate_icons.dart';
|
||||
@@ -125,8 +126,9 @@ class _MonthlyDonationGoalCard extends StatelessWidget {
|
||||
colorScheme.surface,
|
||||
);
|
||||
|
||||
return Card(
|
||||
return AppContentCard(
|
||||
elevation: 0,
|
||||
mornyeSurface: false,
|
||||
color: cardColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
child: Padding(
|
||||
@@ -260,8 +262,9 @@ class _DonateLinksCard extends StatelessWidget {
|
||||
colorScheme.surface,
|
||||
);
|
||||
|
||||
return Card(
|
||||
return AppContentCard(
|
||||
elevation: 0,
|
||||
mornyeSurface: false,
|
||||
color: cardColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
@@ -325,8 +328,9 @@ class _RecentSupportersCard extends StatelessWidget {
|
||||
final colorScheme = theme.colorScheme;
|
||||
final isDark = theme.brightness == Brightness.dark;
|
||||
|
||||
return Card(
|
||||
return AppContentCard(
|
||||
elevation: 0,
|
||||
mornyeSurface: false,
|
||||
color: Color.alphaBlend(
|
||||
(isDark ? Colors.white : Colors.black).withValues(
|
||||
alpha: isDark ? 0.08 : 0.04,
|
||||
@@ -423,8 +427,9 @@ class _DonateNoticeCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
return AppContentCard(
|
||||
elevation: 0,
|
||||
mornyeSurface: false,
|
||||
color: colorScheme.secondaryContainer.withValues(alpha: 0.3),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
child: Padding(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:spotiflac_android/widgets/app_alert_dialog.dart';
|
||||
import 'package:spotiflac_android/widgets/app_bottom_sheet.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/expressive_button.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:share_plus/share_plus.dart' show ShareParams, SharePlus;
|
||||
@@ -732,8 +733,9 @@ class _LogSummaryCard extends StatelessWidget {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: Card(
|
||||
child: AppContentCard(
|
||||
elevation: 0,
|
||||
mornyeSurface: false,
|
||||
color: analysis.hasISPBlocking
|
||||
? colorScheme.errorContainer.withValues(alpha: 0.5)
|
||||
: colorScheme.tertiaryContainer.withValues(alpha: 0.5),
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:material_3_expressive/material_3_expressive.dart';
|
||||
import 'package:spotiflac_android/theme/app_motion.dart';
|
||||
import 'package:spotiflac_android/theme/material_expressive.dart';
|
||||
import 'package:spotiflac_android/widgets/app_alert_dialog.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/app_icon.dart';
|
||||
import 'package:spotiflac_android/widgets/app_icon_button.dart';
|
||||
import 'package:spotiflac_android/widgets/app_loading_indicator.dart';
|
||||
@@ -1145,8 +1146,9 @@ class _SetupScreenState extends ConsumerState<SetupScreen>
|
||||
child: Column(
|
||||
children: [
|
||||
if (_selectedDirectory != null)
|
||||
Card(
|
||||
AppContentCard(
|
||||
color: colorScheme.secondaryContainer,
|
||||
mornyeSurface: false,
|
||||
child: ListTile(
|
||||
leading: Icon(
|
||||
Icons.folder,
|
||||
|
||||
@@ -24,6 +24,7 @@ import 'package:spotiflac_android/utils/logger.dart';
|
||||
import 'package:spotiflac_android/widgets/app_action_button.dart';
|
||||
import 'package:spotiflac_android/widgets/app_bottom_sheet.dart';
|
||||
import 'package:spotiflac_android/widgets/app_choice_chip.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/app_progress_indicator.dart';
|
||||
import 'package:spotiflac_android/widgets/app_icon_button.dart';
|
||||
import 'package:spotiflac_android/widgets/app_loading_indicator.dart';
|
||||
@@ -2019,7 +2020,7 @@ class _EditMetadataSheetState extends State<EditMetadataSheet> {
|
||||
child: content,
|
||||
),
|
||||
)
|
||||
: Card(
|
||||
: AppContentCard(
|
||||
elevation: 0,
|
||||
margin: EdgeInsets.zero,
|
||||
color: settingsGroupColor(context),
|
||||
|
||||
@@ -13,6 +13,10 @@ class AppContentCard extends StatelessWidget {
|
||||
this.elevation,
|
||||
this.clipBehavior = Clip.none,
|
||||
this.preserveColor = false,
|
||||
this.margin,
|
||||
this.onTap,
|
||||
this.onLongPress,
|
||||
this.mornyeSurface = true,
|
||||
});
|
||||
|
||||
final Widget child;
|
||||
@@ -22,51 +26,81 @@ class AppContentCard extends StatelessWidget {
|
||||
final Clip clipBehavior;
|
||||
final bool preserveColor;
|
||||
|
||||
/// Outer margin; Material defaults to its card theme, Mornye to zero.
|
||||
final EdgeInsetsGeometry? margin;
|
||||
|
||||
/// Makes the whole card pressable, with the expressive press feedback.
|
||||
final VoidCallback? onTap;
|
||||
final VoidCallback? onLongPress;
|
||||
|
||||
/// False keeps a plain [Card] under Mornye for surfaces that carry their
|
||||
/// own tinted color (errors, notices) rather than the Mornye control fill.
|
||||
final bool mornyeSurface;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!context.isMornye) {
|
||||
if (materialExpressiveEnabled(context) &&
|
||||
if (!context.isMornye || !mornyeSurface) {
|
||||
if (!context.isMornye &&
|
||||
materialExpressiveEnabled(context) &&
|
||||
(shape == null || shape is RoundedRectangleBorder)) {
|
||||
final radius = shape is RoundedRectangleBorder
|
||||
? (shape as RoundedRectangleBorder).borderRadius.resolve(
|
||||
Directionality.of(context),
|
||||
)
|
||||
: BorderRadius.circular(20);
|
||||
return Padding(
|
||||
padding: CardTheme.of(context).margin ?? const EdgeInsets.all(4),
|
||||
padding:
|
||||
margin ?? CardTheme.of(context).margin ?? const EdgeInsets.all(4),
|
||||
child: MaterialExpressiveScope(
|
||||
child: M3ECard(
|
||||
variant: M3ECardVariant.filled,
|
||||
padding: EdgeInsets.zero,
|
||||
color: color,
|
||||
elevation: elevation ?? 0,
|
||||
border: shape is RoundedRectangleBorder
|
||||
// BorderSide.none would still paint as a hairline.
|
||||
border:
|
||||
shape is RoundedRectangleBorder &&
|
||||
(shape as RoundedRectangleBorder).side.style !=
|
||||
BorderStyle.none
|
||||
? (shape as RoundedRectangleBorder).side
|
||||
: null,
|
||||
borderRadius: shape is RoundedRectangleBorder
|
||||
? (shape as RoundedRectangleBorder).borderRadius.resolve(
|
||||
Directionality.of(context),
|
||||
)
|
||||
: BorderRadius.circular(20),
|
||||
clipBehavior: clipBehavior,
|
||||
borderRadius: radius,
|
||||
clipBehavior: onTap == null && onLongPress == null
|
||||
? clipBehavior
|
||||
: Clip.antiAlias,
|
||||
onPressed: onTap,
|
||||
onLongPress: onLongPress,
|
||||
child: Material(color: Colors.transparent, child: child),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
final interactive = onTap != null || onLongPress != null;
|
||||
return Card(
|
||||
color: color,
|
||||
shape: shape,
|
||||
elevation: elevation,
|
||||
clipBehavior: clipBehavior,
|
||||
child: child,
|
||||
margin: margin,
|
||||
clipBehavior: interactive ? Clip.antiAlias : clipBehavior,
|
||||
child: interactive
|
||||
? InkWell(onTap: onTap, onLongPress: onLongPress, child: child)
|
||||
: child,
|
||||
);
|
||||
}
|
||||
return Material(
|
||||
final content = DividerTheme(
|
||||
data: DividerTheme.of(
|
||||
context,
|
||||
).copyWith(color: MornyeTheme.metadataDividerColor(context)),
|
||||
child: child,
|
||||
);
|
||||
final surface = Material(
|
||||
color: preserveColor ? color : MornyeTheme.controlFill(context),
|
||||
borderRadius: BorderRadius.circular(28),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: DividerTheme(
|
||||
data: DividerTheme.of(
|
||||
context,
|
||||
).copyWith(color: MornyeTheme.metadataDividerColor(context)),
|
||||
child: child,
|
||||
),
|
||||
child: onTap != null || onLongPress != null
|
||||
? InkWell(onTap: onTap, onLongPress: onLongPress, child: content)
|
||||
: content,
|
||||
);
|
||||
return margin == null ? surface : Padding(padding: margin!, child: surface);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/theme/app_tokens.dart';
|
||||
import 'package:spotiflac_android/theme/mornye_theme.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/app_icon.dart';
|
||||
import 'package:spotiflac_android/widgets/app_shape.dart';
|
||||
import 'package:spotiflac_android/widgets/expressive_button.dart';
|
||||
@@ -47,9 +48,10 @@ class ErrorCard extends StatelessWidget {
|
||||
? colorScheme.onErrorContainer
|
||||
: colorScheme.error;
|
||||
|
||||
return Card(
|
||||
return AppContentCard(
|
||||
elevation: 0,
|
||||
color: background,
|
||||
mornyeSurface: false,
|
||||
shape: RoundedRectangleBorder(borderRadius: tokens.borderRadiusCard),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(tokens.gapLg),
|
||||
|
||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:spotiflac_android/providers/runtime_profile_provider.dart';
|
||||
import 'package:spotiflac_android/theme/mornye_theme.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/mornye_chrome.dart';
|
||||
|
||||
/// Repo cards share the Mornye glass material, including their detail pages.
|
||||
@@ -22,7 +23,7 @@ class ExtensionRepoCard extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
if (!context.isMornye) {
|
||||
return Card(
|
||||
return AppContentCard(
|
||||
elevation: 0,
|
||||
color: color,
|
||||
margin: margin,
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/theme/app_motion.dart';
|
||||
import 'package:spotiflac_android/theme/mornye_theme.dart';
|
||||
import 'package:spotiflac_android/utils/isrc_utils.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/app_icon.dart';
|
||||
import 'package:spotiflac_android/widgets/mornye_player_details_sheet.dart';
|
||||
import 'package:spotiflac_android/widgets/settings_group.dart';
|
||||
@@ -108,7 +109,7 @@ class _MetadataList extends StatelessWidget {
|
||||
controller: scrollController,
|
||||
padding: const EdgeInsets.fromLTRB(16, 4, 16, 24),
|
||||
children: [
|
||||
Card(
|
||||
AppContentCard(
|
||||
elevation: 0,
|
||||
color: settingsGroupColor(context),
|
||||
shape: RoundedRectangleBorder(
|
||||
|
||||
+55
-55
@@ -3,6 +3,7 @@ import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/theme/app_tokens.dart';
|
||||
import 'package:spotiflac_android/theme/mornye_theme.dart';
|
||||
import 'package:spotiflac_android/widgets/animation_utils.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/settings_group.dart';
|
||||
|
||||
/// How a [TrackCard] paints its container.
|
||||
@@ -145,11 +146,13 @@ class TrackCard extends StatelessWidget {
|
||||
} else if (style == TrackCardStyle.flat || context.isMornye) {
|
||||
cardColor = Colors.transparent;
|
||||
} else {
|
||||
cardColor = null; // Inherits cardTheme.color.
|
||||
// The card theme's tone, which the expressive card does not read.
|
||||
cardColor = theme.cardTheme.color;
|
||||
}
|
||||
|
||||
final card = Card(
|
||||
final card = AppContentCard(
|
||||
elevation: 0,
|
||||
mornyeSurface: false,
|
||||
// The two styles carry their own outer margin so no call site has to
|
||||
// repeat it: flat rows sit tight inside a collection list, filled cards
|
||||
// get the Material list margin.
|
||||
@@ -165,63 +168,60 @@ class TrackCard extends StatelessWidget {
|
||||
color: cardColor,
|
||||
clipBehavior: Clip.antiAlias,
|
||||
shape: RoundedRectangleBorder(borderRadius: borderRadius),
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
borderRadius: borderRadius,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (background != null) Positioned.fill(child: background!),
|
||||
Padding(
|
||||
padding: contentPadding,
|
||||
child: Row(
|
||||
children: [
|
||||
if (isSelectionMode) ...[
|
||||
Semantics(
|
||||
checked: isSelected,
|
||||
label: isSelected
|
||||
? context.l10n.a11yDeselectTrack
|
||||
: context.l10n.a11ySelectTrack,
|
||||
child: AnimatedSelectionCheckbox(
|
||||
visible: true,
|
||||
selected: isSelected,
|
||||
colorScheme: colorScheme,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
SizedBox(width: tokens.gapMd),
|
||||
],
|
||||
leading,
|
||||
SizedBox(width: tokens.gapMd),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
if (subtitle != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
subtitle!,
|
||||
],
|
||||
],
|
||||
onTap: onTap,
|
||||
onLongPress: onLongPress,
|
||||
child: Stack(
|
||||
children: [
|
||||
if (background != null) Positioned.fill(child: background!),
|
||||
Padding(
|
||||
padding: contentPadding,
|
||||
child: Row(
|
||||
children: [
|
||||
if (isSelectionMode) ...[
|
||||
Semantics(
|
||||
checked: isSelected,
|
||||
label: isSelected
|
||||
? context.l10n.a11yDeselectTrack
|
||||
: context.l10n.a11ySelectTrack,
|
||||
child: AnimatedSelectionCheckbox(
|
||||
visible: true,
|
||||
selected: isSelected,
|
||||
colorScheme: colorScheme,
|
||||
size: 24,
|
||||
),
|
||||
),
|
||||
if (trailing != null && !isSelectionMode) ...[
|
||||
SizedBox(width: tokens.gapSm),
|
||||
trailing!,
|
||||
],
|
||||
SizedBox(width: tokens.gapMd),
|
||||
],
|
||||
),
|
||||
leading,
|
||||
SizedBox(width: tokens.gapMd),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.bodyLarge?.copyWith(
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
if (subtitle != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
subtitle!,
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
if (trailing != null && !isSelectionMode) ...[
|
||||
SizedBox(width: tokens.gapSm),
|
||||
trailing!,
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (!context.isMornye) return card;
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:spotiflac_android/theme/app_tokens.dart';
|
||||
import 'package:spotiflac_android/theme/mornye_theme.dart';
|
||||
import 'package:spotiflac_android/theme/cover_palette.dart';
|
||||
import 'package:spotiflac_android/widgets/app_bottom_sheet.dart';
|
||||
import 'package:spotiflac_android/widgets/app_content_card.dart';
|
||||
import 'package:spotiflac_android/widgets/album_detail_header.dart';
|
||||
import 'package:spotiflac_android/widgets/app_search_field.dart';
|
||||
import 'package:spotiflac_android/widgets/mornye_chrome.dart';
|
||||
@@ -717,7 +718,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
final card = tester.widget<Card>(find.byType(Card));
|
||||
final card = tester.widget<AppContentCard>(find.byType(AppContentCard));
|
||||
expect(card.color, Colors.transparent);
|
||||
});
|
||||
|
||||
@@ -739,9 +740,9 @@ void main() {
|
||||
),
|
||||
);
|
||||
|
||||
final cardRect = tester.getRect(find.byType(Card));
|
||||
final cardRect = tester.getRect(find.byType(AppContentCard));
|
||||
final trailingRect = tester.getRect(find.byKey(trailingKey));
|
||||
// Card's render box includes its 8dp flat-row margin; content itself is
|
||||
// The card's render box includes its 8dp flat-row margin; content is
|
||||
// inset another 6dp from the painted card edge.
|
||||
expect(cardRect.right - trailingRect.right, 14);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user