From 146b2acd4d3ebb4633b1ee1dab9fa05c1f993dc2 Mon Sep 17 00:00:00 2001 From: zarzet <42882290+zarzet@users.noreply.github.com> Date: Wed, 23 Sep 2026 21:53:15 +0700 Subject: [PATCH] refactor(ui): unify theme styling and enlarge profile button Centralize existing Material and Mornye layout metrics in AppTokens and share adaptive icon selection. Enlarge the Home profile avatar to 44 dp with a 56 dp touch target. --- lib/screens/album_screen.dart | 3 +- lib/screens/downloaded_album_screen.dart | 3 +- lib/screens/home_tab_widgets.dart | 6 +- lib/screens/local_album_screen.dart | 3 +- lib/screens/now_playing_screen.dart | 13 ++- .../repo/extension_details_screen.dart | 4 +- lib/screens/settings/about_page.dart | 2 +- lib/screens/settings/app_settings_page.dart | 8 +- .../settings/appearance_settings_page.dart | 2 +- .../settings/extension_detail_page.dart | 5 +- lib/screens/track_metadata_edit_sheet.dart | 8 +- lib/screens/upgrade_intro_screen.dart | 2 +- lib/theme/app_tokens.dart | 110 ++++++++++++++++++ lib/theme/mornye_icons.dart | 6 + lib/theme/mornye_theme.dart | 13 +++ lib/widgets/animation_utils.dart | 3 +- lib/widgets/app_alert_dialog.dart | 3 +- lib/widgets/mini_player.dart | 5 +- lib/widgets/profile_avatar.dart | 6 +- lib/widgets/settings_group.dart | 51 ++++---- 20 files changed, 191 insertions(+), 65 deletions(-) diff --git a/lib/screens/album_screen.dart b/lib/screens/album_screen.dart index 586e1eba..cb55e744 100644 --- a/lib/screens/album_screen.dart +++ b/lib/screens/album_screen.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart' show CupertinoIcons; +import 'package:spotiflac_android/theme/app_tokens.dart'; import 'package:spotiflac_android/theme/mornye_theme.dart'; import 'package:spotiflac_android/screens/track_history_snapshot.dart'; import 'package:spotiflac_android/widgets/collection_scaffold.dart'; @@ -514,7 +515,7 @@ class _AlbumScreenState extends ConsumerState extensionId: widget.extensionId, style: TextStyle( color: colorScheme.primary, - fontSize: context.isMornye ? 20 : 16, + fontSize: context.tokens.headerSubtitleSize, fontWeight: context.isMornye ? FontWeight.w400 : FontWeight.w600, diff --git a/lib/screens/downloaded_album_screen.dart b/lib/screens/downloaded_album_screen.dart index 52cd60e0..6ec56726 100644 --- a/lib/screens/downloaded_album_screen.dart +++ b/lib/screens/downloaded_album_screen.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'dart:math'; import 'package:flutter/material.dart'; +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:flutter_riverpod/flutter_riverpod.dart'; @@ -395,7 +396,7 @@ class _DownloadedAlbumScreenState extends ConsumerState color: context.isMornye ? colorScheme.primary : HeaderPalette.of(context).onSurfaceVariant, - fontSize: context.isMornye ? 20 : 16, + fontSize: context.tokens.headerSubtitleSize, fontWeight: context.isMornye ? FontWeight.w400 : FontWeight.w600, ), textAlign: TextAlign.center, diff --git a/lib/screens/home_tab_widgets.dart b/lib/screens/home_tab_widgets.dart index 0238f796..a3d22398 100644 --- a/lib/screens/home_tab_widgets.dart +++ b/lib/screens/home_tab_widgets.dart @@ -274,9 +274,9 @@ class _TrackItemWithStatus extends ConsumerWidget { child: Padding( padding: EdgeInsets.fromLTRB( 12, - context.isMornye ? 12 : 10, + context.tokens.trackRowPaddingV, 6, - context.isMornye ? 12 : 10, + context.tokens.trackRowPaddingV, ), child: Row( children: [ @@ -494,7 +494,7 @@ class _CollectionItemWidget extends StatelessWidget { child: Padding( padding: EdgeInsets.symmetric( horizontal: 12, - vertical: context.isMornye ? 12 : 10, + vertical: context.tokens.trackRowPaddingV, ), child: Row( children: [ diff --git a/lib/screens/local_album_screen.dart b/lib/screens/local_album_screen.dart index c70fad3f..4ebd3d67 100644 --- a/lib/screens/local_album_screen.dart +++ b/lib/screens/local_album_screen.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:spotiflac_android/theme/app_tokens.dart'; import 'package:spotiflac_android/theme/mornye_theme.dart'; import 'package:spotiflac_android/services/local_track_batch_actions.dart'; import 'package:spotiflac_android/theme/cover_palette.dart'; @@ -274,7 +275,7 @@ class _LocalAlbumScreenState extends ConsumerState color: context.isMornye ? colorScheme.primary : HeaderPalette.of(context).onSurfaceVariant, - fontSize: context.isMornye ? 20 : 16, + fontSize: context.tokens.headerSubtitleSize, fontWeight: context.isMornye ? FontWeight.w400 : FontWeight.w600, ), textAlign: TextAlign.center, diff --git a/lib/screens/now_playing_screen.dart b/lib/screens/now_playing_screen.dart index 13a2a719..38780e43 100644 --- a/lib/screens/now_playing_screen.dart +++ b/lib/screens/now_playing_screen.dart @@ -15,6 +15,7 @@ import 'package:spotiflac_android/screens/downloaded_album_screen.dart'; import 'package:spotiflac_android/screens/local_album_screen.dart'; import 'package:spotiflac_android/services/library_database.dart'; import 'package:spotiflac_android/services/music_player_service.dart'; +import 'package:spotiflac_android/theme/app_tokens.dart'; import 'package:spotiflac_android/theme/mornye_theme.dart'; import 'package:spotiflac_android/utils/clickable_metadata.dart'; import 'package:spotiflac_android/utils/file_access.dart'; @@ -2034,7 +2035,7 @@ class _PlaybackControls extends ConsumerWidget { icon: const Icon(Icons.skip_previous), onPressed: controller.previous, ), - SizedBox(width: mornye ? 12 : 20), + SizedBox(width: context.tokens.playerControlGap), if (mornye) MornyePlaybackButton( icon: isPlaying @@ -2073,7 +2074,7 @@ class _PlaybackControls extends ConsumerWidget { : () => controller.togglePlayPause(isPlaying), ), ), - SizedBox(width: mornye ? 12 : 20), + SizedBox(width: context.tokens.playerControlGap), if (mornye) MornyePlaybackButton( icon: CupertinoIcons.forward_fill, @@ -2496,7 +2497,7 @@ class _SyncedLyricsViewState extends ConsumerState<_SyncedLyricsView> { ? Theme.of(context).textTheme.headlineSmall : Theme.of(context).textTheme.titleLarge) ?.copyWith( - height: mornye ? 1.3 : 1.4, + height: context.tokens.lyricsLineHeight, fontSize: mornye ? 28 : null, fontWeight: mornye || isActive ? FontWeight.bold @@ -2543,7 +2544,9 @@ class _SyncedLyricsViewState extends ConsumerState<_SyncedLyricsView> { return Padding( key: _lineKeys[index], - padding: EdgeInsets.symmetric(vertical: mornye ? 16 : 10), + padding: EdgeInsets.symmetric( + vertical: context.tokens.lyricsLinePaddingV, + ), child: GestureDetector( onTap: () => ref.read(musicPlayerControllerProvider).seek(line.time), @@ -2785,7 +2788,7 @@ class _WordHighlightedLyricLineState (Theme.of(context).textTheme.headlineSmall ?? const TextStyle()) .copyWith( fontSize: context.isMornye ? 28 : null, - height: context.isMornye ? 1.3 : 1.4, + height: context.tokens.lyricsLineHeight, fontWeight: FontWeight.bold, ); final primary = widget.line.hasWordTiming diff --git a/lib/screens/repo/extension_details_screen.dart b/lib/screens/repo/extension_details_screen.dart index 60cccfe9..bdcbc1da 100644 --- a/lib/screens/repo/extension_details_screen.dart +++ b/lib/screens/repo/extension_details_screen.dart @@ -332,7 +332,7 @@ class _ExtensionDetailsScreenState child: Row( children: [ Icon( - context.isMornye ? mornyeIconFor(icon) : icon, + context.adaptiveIcon(icon), size: 20, color: colorScheme.primary, ), @@ -768,7 +768,7 @@ class _CapabilityRow extends StatelessWidget { child: Row( children: [ Icon( - context.isMornye ? mornyeIconFor(icon) : icon, + context.adaptiveIcon(icon), size: 20, color: enabled ? colorScheme.primary : colorScheme.outline, ), diff --git a/lib/screens/settings/about_page.dart b/lib/screens/settings/about_page.dart index 6c4d7a6c..7b4a41a0 100644 --- a/lib/screens/settings/about_page.dart +++ b/lib/screens/settings/about_page.dart @@ -697,7 +697,7 @@ class _AboutSettingsItem extends StatelessWidget { width: 40, height: 40, child: Icon( - context.isMornye ? mornyeIconFor(icon) : icon, + context.adaptiveIcon(icon), color: context.isMornye ? colorScheme.primary : colorScheme.onSurfaceVariant, diff --git a/lib/screens/settings/app_settings_page.dart b/lib/screens/settings/app_settings_page.dart index 7986eb12..75b1a0f5 100644 --- a/lib/screens/settings/app_settings_page.dart +++ b/lib/screens/settings/app_settings_page.dart @@ -282,9 +282,7 @@ class _UpdateChannelSelector extends StatelessWidget { Row( children: [ Icon( - context.isMornye - ? mornyeIconFor(Icons.new_releases) - : Icons.new_releases, + context.adaptiveIcon(Icons.new_releases), color: context.isMornye ? colorScheme.primary : colorScheme.onSurfaceVariant, @@ -389,9 +387,7 @@ class _VerificationBrowserModeSelector extends StatelessWidget { Row( children: [ Icon( - context.isMornye - ? mornyeIconFor(Icons.open_in_browser) - : Icons.open_in_browser, + context.adaptiveIcon(Icons.open_in_browser), color: context.isMornye ? colorScheme.primary : colorScheme.onSurfaceVariant, diff --git a/lib/screens/settings/appearance_settings_page.dart b/lib/screens/settings/appearance_settings_page.dart index 04c2e530..b26c9420 100644 --- a/lib/screens/settings/appearance_settings_page.dart +++ b/lib/screens/settings/appearance_settings_page.dart @@ -709,7 +709,7 @@ class _LanguageSelector extends StatelessWidget { final colorScheme = Theme.of(context).colorScheme; final content = ListTile( leading: Icon( - context.isMornye ? mornyeIconFor(Icons.language) : Icons.language, + context.adaptiveIcon(Icons.language), color: context.isMornye ? colorScheme.primary : colorScheme.onSurfaceVariant, diff --git a/lib/screens/settings/extension_detail_page.dart b/lib/screens/settings/extension_detail_page.dart index 73cad455..43966921 100644 --- a/lib/screens/settings/extension_detail_page.dart +++ b/lib/screens/settings/extension_detail_page.dart @@ -594,7 +594,6 @@ class _ExtensionDivider extends StatelessWidget { @override Widget build(BuildContext context) => Divider( height: 1, - thickness: context.isMornye ? 0.5 : 1, indent: indent, endIndent: 16, color: context.isMornye @@ -706,7 +705,7 @@ class _CapabilityItem extends StatelessWidget { child: Row( children: [ Icon( - context.isMornye ? mornyeIconFor(icon) : icon, + context.adaptiveIcon(icon), color: context.isMornye || enabled ? colorScheme.primary : colorScheme.outline, @@ -980,7 +979,7 @@ class _PermissionItem extends StatelessWidget { child: Row( children: [ Icon( - context.isMornye ? mornyeIconFor(icon) : icon, + context.adaptiveIcon(icon), color: context.isMornye ? colorScheme.primary : colorScheme.onSurfaceVariant, diff --git a/lib/screens/track_metadata_edit_sheet.dart b/lib/screens/track_metadata_edit_sheet.dart index f97a5c5a..4b84aedd 100644 --- a/lib/screens/track_metadata_edit_sheet.dart +++ b/lib/screens/track_metadata_edit_sheet.dart @@ -438,7 +438,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> { vertical: 4, ), leading: Icon( - context.isMornye ? mornyeIconFor(icon) : icon, + context.adaptiveIcon(icon), color: selected ? cs.primary : cs.onSurfaceVariant, ), title: Text(name, maxLines: 2, overflow: TextOverflow.ellipsis), @@ -3165,11 +3165,7 @@ class _EditMetadataSheetState extends State<_EditMetadataSheet> { final headerRow = Row( children: [ - Icon( - context.isMornye ? mornyeIconFor(icon) : icon, - size: 20, - color: cs.primary, - ), + Icon(context.adaptiveIcon(icon), size: 20, color: cs.primary), const SizedBox(width: 10), Expanded( child: Text( diff --git a/lib/screens/upgrade_intro_screen.dart b/lib/screens/upgrade_intro_screen.dart index 3d9f55c5..ea64d5d0 100644 --- a/lib/screens/upgrade_intro_screen.dart +++ b/lib/screens/upgrade_intro_screen.dart @@ -486,7 +486,7 @@ class _TourFeature extends StatelessWidget { SizedBox( width: 36, child: Icon( - context.isMornye ? mornyeIconFor(icon) : icon, + context.adaptiveIcon(icon), size: 28, color: theme.colorScheme.primary, ), diff --git a/lib/theme/app_tokens.dart b/lib/theme/app_tokens.dart index 9f45fb53..9ad74360 100644 --- a/lib/theme/app_tokens.dart +++ b/lib/theme/app_tokens.dart @@ -33,6 +33,19 @@ class AppTokens extends ThemeExtension { required this.motionFast, required this.motionMedium, required this.motionSlow, + this.rowPaddingH = 20, + this.rowPaddingV = 16, + this.rowPaddingVCompact = 12, + this.rowIconGap = 16, + this.rowIconDividerIndent = 56, + this.rowChevronSize = 24, + this.rowMinHeight = 0, + this.trackRowPaddingV = 10, + this.headerSubtitleSize = 16, + this.lyricsLineHeight = 1.4, + this.lyricsLinePaddingV = 10, + this.playerControlGap = 20, + this.dialogInsetH = 40, }); /// The values every theme in the app uses today. Kept as the single @@ -121,6 +134,48 @@ class AppTokens extends ThemeExtension { final Duration motionMedium; final Duration motionSlow; + /// Horizontal inset of settings-style rows; also their divider end indent. + final double rowPaddingH; + + /// Vertical inset of tappable settings rows. + final double rowPaddingV; + + /// Vertical inset of rows that already carry a tall trailing control + /// (switches). + final double rowPaddingVCompact; + + /// Space between a row's leading icon and its text. + final double rowIconGap; + + /// Divider start indent for rows with a leading icon, so the line starts + /// under the text instead of under the icon. + final double rowIconDividerIndent; + + /// Disclosure chevron shown on navigable rows. + final double rowChevronSize; + + /// Minimum content height of a settings row. + final double rowMinHeight; + + /// Vertical inset of track rows in feeds, search results and their + /// loading skeletons. + final double trackRowPaddingV; + + /// Artist line under the title in album/collection headers. + final double headerSubtitleSize; + + /// Line height multiplier for synced lyrics. + final double lyricsLineHeight; + + /// Vertical space around each synced lyric line. + final double lyricsLinePaddingV; + + /// Gap between the previous / play / next transport controls. + final double playerControlGap; + + /// Horizontal screen inset of app dialogs. + final double dialogInsetH; + BorderRadius get borderRadiusBadge => BorderRadius.circular(radiusBadge); BorderRadius get borderRadiusThumb => BorderRadius.circular(radiusThumb); BorderRadius get borderRadiusCover => BorderRadius.circular(radiusCover); @@ -156,6 +211,19 @@ class AppTokens extends ThemeExtension { Duration? motionFast, Duration? motionMedium, Duration? motionSlow, + double? rowPaddingH, + double? rowPaddingV, + double? rowPaddingVCompact, + double? rowIconGap, + double? rowIconDividerIndent, + double? rowChevronSize, + double? rowMinHeight, + double? trackRowPaddingV, + double? headerSubtitleSize, + double? lyricsLineHeight, + double? lyricsLinePaddingV, + double? playerControlGap, + double? dialogInsetH, }) { return AppTokens( radiusBadge: radiusBadge ?? this.radiusBadge, @@ -183,6 +251,19 @@ class AppTokens extends ThemeExtension { motionFast: motionFast ?? this.motionFast, motionMedium: motionMedium ?? this.motionMedium, motionSlow: motionSlow ?? this.motionSlow, + rowPaddingH: rowPaddingH ?? this.rowPaddingH, + rowPaddingV: rowPaddingV ?? this.rowPaddingV, + rowPaddingVCompact: rowPaddingVCompact ?? this.rowPaddingVCompact, + rowIconGap: rowIconGap ?? this.rowIconGap, + rowIconDividerIndent: rowIconDividerIndent ?? this.rowIconDividerIndent, + rowChevronSize: rowChevronSize ?? this.rowChevronSize, + rowMinHeight: rowMinHeight ?? this.rowMinHeight, + trackRowPaddingV: trackRowPaddingV ?? this.trackRowPaddingV, + headerSubtitleSize: headerSubtitleSize ?? this.headerSubtitleSize, + lyricsLineHeight: lyricsLineHeight ?? this.lyricsLineHeight, + lyricsLinePaddingV: lyricsLinePaddingV ?? this.lyricsLinePaddingV, + playerControlGap: playerControlGap ?? this.playerControlGap, + dialogInsetH: dialogInsetH ?? this.dialogInsetH, ); } @@ -226,6 +307,35 @@ class AppTokens extends ThemeExtension { motionFast: t < 0.5 ? motionFast : other.motionFast, motionMedium: t < 0.5 ? motionMedium : other.motionMedium, motionSlow: t < 0.5 ? motionSlow : other.motionSlow, + rowPaddingH: lerpDouble(rowPaddingH, other.rowPaddingH, t), + rowPaddingV: lerpDouble(rowPaddingV, other.rowPaddingV, t), + rowPaddingVCompact: lerpDouble( + rowPaddingVCompact, + other.rowPaddingVCompact, + t, + ), + rowIconGap: lerpDouble(rowIconGap, other.rowIconGap, t), + rowIconDividerIndent: lerpDouble( + rowIconDividerIndent, + other.rowIconDividerIndent, + t, + ), + rowChevronSize: lerpDouble(rowChevronSize, other.rowChevronSize, t), + rowMinHeight: lerpDouble(rowMinHeight, other.rowMinHeight, t), + trackRowPaddingV: lerpDouble(trackRowPaddingV, other.trackRowPaddingV, t), + headerSubtitleSize: lerpDouble( + headerSubtitleSize, + other.headerSubtitleSize, + t, + ), + lyricsLineHeight: lerpDouble(lyricsLineHeight, other.lyricsLineHeight, t), + lyricsLinePaddingV: lerpDouble( + lyricsLinePaddingV, + other.lyricsLinePaddingV, + t, + ), + playerControlGap: lerpDouble(playerControlGap, other.playerControlGap, t), + dialogInsetH: lerpDouble(dialogInsetH, other.dialogInsetH, t), ); } diff --git a/lib/theme/mornye_icons.dart b/lib/theme/mornye_icons.dart index a0159e14..4536b384 100644 --- a/lib/theme/mornye_icons.dart +++ b/lib/theme/mornye_icons.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart' show CupertinoIcons; import 'package:flutter/material.dart'; +import 'package:spotiflac_android/theme/mornye_theme.dart'; /// Cupertino counterparts for the shared Settings and extension UI symbols. IconData mornyeIconFor(IconData icon) { @@ -196,3 +197,8 @@ IconData mornyeIconFor(IconData icon) { _ => icon, }; } + +extension MornyeIconContext on BuildContext { + /// [icon] as-is, or its Cupertino counterpart when the Mornye theme is on. + IconData adaptiveIcon(IconData icon) => isMornye ? mornyeIconFor(icon) : icon; +} diff --git a/lib/theme/mornye_theme.dart b/lib/theme/mornye_theme.dart index 4ea4b9e3..977abaa6 100644 --- a/lib/theme/mornye_theme.dart +++ b/lib/theme/mornye_theme.dart @@ -53,6 +53,19 @@ class MornyeTheme extends ThemeExtension { motionFast: const Duration(milliseconds: 180), motionMedium: const Duration(milliseconds: 220), motionSlow: const Duration(milliseconds: 380), + rowPaddingH: 16, + rowPaddingV: 10, + rowPaddingVCompact: 8, + rowIconGap: 12, + rowIconDividerIndent: 52, + rowChevronSize: 18, + rowMinHeight: 28, + trackRowPaddingV: 12, + headerSubtitleSize: 20, + lyricsLineHeight: 1.3, + lyricsLinePaddingV: 16, + playerControlGap: 12, + dialogInsetH: 24, ); // Theme construction includes seeded color generation and typography. Reuse diff --git a/lib/widgets/animation_utils.dart b/lib/widgets/animation_utils.dart index 8491b138..45f22215 100644 --- a/lib/widgets/animation_utils.dart +++ b/lib/widgets/animation_utils.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:spotiflac_android/theme/app_tokens.dart'; import 'package:spotiflac_android/theme/mornye_theme.dart'; /// Wraps a child in a staggered fade-in + slide-up animation. @@ -841,7 +842,7 @@ class HomeSearchSkeleton extends StatelessWidget { return Padding( padding: EdgeInsets.symmetric( horizontal: mornye ? 12 : 16, - vertical: mornye ? 12 : 10, + vertical: context.tokens.trackRowPaddingV, ), child: Row( children: [ diff --git a/lib/widgets/app_alert_dialog.dart b/lib/widgets/app_alert_dialog.dart index c2497212..00ec8ff2 100644 --- a/lib/widgets/app_alert_dialog.dart +++ b/lib/widgets/app_alert_dialog.dart @@ -1,5 +1,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:spotiflac_android/theme/app_tokens.dart'; import 'package:spotiflac_android/theme/mornye_theme.dart'; import 'package:spotiflac_android/widgets/mornye_chrome.dart'; @@ -53,7 +54,7 @@ class AppDialogSurface extends StatelessWidget { elevation: mornye ? 0 : null, shape: shape, insetPadding: EdgeInsets.symmetric( - horizontal: mornye ? 24 : 40, + horizontal: context.tokens.dialogInsetH, vertical: 24, ), child: ConstrainedBox( diff --git a/lib/widgets/mini_player.dart b/lib/widgets/mini_player.dart index ecc46925..eb7160f3 100644 --- a/lib/widgets/mini_player.dart +++ b/lib/widgets/mini_player.dart @@ -6,6 +6,7 @@ import 'package:spotiflac_android/providers/music_player_provider.dart'; import 'package:spotiflac_android/providers/player_motion_artwork_provider.dart'; import 'package:spotiflac_android/providers/player_artwork_video_provider.dart'; import 'package:spotiflac_android/providers/runtime_profile_provider.dart'; +import 'package:spotiflac_android/theme/app_tokens.dart'; import 'package:spotiflac_android/theme/mornye_theme.dart'; import 'package:spotiflac_android/screens/now_playing_screen.dart'; import 'package:spotiflac_android/utils/string_utils.dart'; @@ -108,8 +109,8 @@ class _MiniPlayerState extends ConsumerState { child: ClipRRect( borderRadius: BorderRadius.circular(6), child: SizedBox( - width: mornye ? 38 : 44, - height: mornye ? 38 : 44, + width: context.tokens.coverMini, + height: context.tokens.coverMini, child: PlayerArtwork( artUri: mediaItem.artUri?.toString(), colorScheme: colorScheme, diff --git a/lib/widgets/profile_avatar.dart b/lib/widgets/profile_avatar.dart index 90ca96c5..b77580f8 100644 --- a/lib/widgets/profile_avatar.dart +++ b/lib/widgets/profile_avatar.dart @@ -92,11 +92,15 @@ class HomeProfileButton extends ConsumerWidget { final profile = ref.watch(userProfileProvider).value; return IconButton( tooltip: context.l10n.settingsTitle, + iconSize: 44, + padding: const EdgeInsets.all(6), + constraints: const BoxConstraints(minWidth: 56, minHeight: 56), + visualDensity: VisualDensity.standard, onPressed: () => ShellNavigationService.requestTab(ShellTab.settings), icon: ProfileAvatar( name: profile?.name ?? '', photoPath: profile?.photoPath, - size: 34, + size: 44, ), ); } diff --git a/lib/widgets/settings_group.dart b/lib/widgets/settings_group.dart index 2d3b012e..85a75500 100644 --- a/lib/widgets/settings_group.dart +++ b/lib/widgets/settings_group.dart @@ -296,22 +296,24 @@ class SettingsItem extends StatelessWidget { highlightColor: Colors.transparent, child: Padding( padding: EdgeInsets.symmetric( - horizontal: context.isMornye ? 16 : 20, - vertical: context.isMornye ? 10 : 16, + horizontal: context.tokens.rowPaddingH, + vertical: context.tokens.rowPaddingV, ), child: ConstrainedBox( - constraints: BoxConstraints(minHeight: context.isMornye ? 28 : 0), + constraints: BoxConstraints( + minHeight: context.tokens.rowMinHeight, + ), child: Row( children: [ if (icon != null) ...[ Icon( - context.isMornye ? mornyeIconFor(icon!) : icon, + context.adaptiveIcon(icon!), color: context.isMornye ? colorScheme.primary : colorScheme.onSurfaceVariant, size: 24, ), - SizedBox(width: context.isMornye ? 12 : 16), + SizedBox(width: context.tokens.rowIconGap), ], Expanded( child: Column( @@ -359,7 +361,7 @@ class SettingsItem extends StatelessWidget { context.isMornye ? CupertinoIcons.chevron_forward : Icons.chevron_right, - size: context.isMornye ? 18 : 24, + size: context.tokens.rowChevronSize, color: colorScheme.onSurfaceVariant, ), ], @@ -371,11 +373,10 @@ class SettingsItem extends StatelessWidget { if (showDivider) Divider( height: 1, - thickness: context.isMornye ? 0.5 : 1, - indent: context.isMornye - ? (icon != null ? 52 : 16) - : (icon != null ? 56 : 20), - endIndent: context.isMornye ? 16 : 20, + indent: icon != null + ? context.tokens.rowIconDividerIndent + : context.tokens.rowPaddingH, + endIndent: context.tokens.rowPaddingH, color: context.isMornye ? colorScheme.outlineVariant : colorScheme.outlineVariant.withValues(alpha: 0.3), @@ -424,14 +425,14 @@ class SettingsSwitchItem extends StatelessWidget { highlightColor: Colors.transparent, child: Padding( padding: EdgeInsets.symmetric( - horizontal: context.isMornye ? 16 : 20, - vertical: context.isMornye ? 8 : 12, + horizontal: context.tokens.rowPaddingH, + vertical: context.tokens.rowPaddingVCompact, ), child: Row( children: [ if (icon != null) ...[ Icon( - context.isMornye ? mornyeIconFor(icon!) : icon, + context.adaptiveIcon(icon!), color: isDisabled ? colorScheme.outline : context.isMornye @@ -439,7 +440,7 @@ class SettingsSwitchItem extends StatelessWidget { : colorScheme.onSurfaceVariant, size: 24, ), - SizedBox(width: context.isMornye ? 12 : 16), + SizedBox(width: context.tokens.rowIconGap), ], Expanded( child: Column( @@ -494,11 +495,10 @@ class SettingsSwitchItem extends StatelessWidget { if (showDivider) Divider( height: 1, - thickness: context.isMornye ? 0.5 : 1, - indent: context.isMornye - ? (icon != null ? 52 : 16) - : (icon != null ? 56 : 20), - endIndent: context.isMornye ? 16 : 20, + indent: icon != null + ? context.tokens.rowIconDividerIndent + : context.tokens.rowPaddingH, + endIndent: context.tokens.rowPaddingH, color: context.isMornye ? colorScheme.outlineVariant : colorScheme.outlineVariant.withValues(alpha: 0.3), @@ -608,10 +608,7 @@ class SettingsChoiceChip extends StatelessWidget { content = Column( mainAxisSize: MainAxisSize.min, children: [ - Icon( - context.isMornye ? mornyeIconFor(icon!) : icon, - color: foreground, - ), + Icon(context.adaptiveIcon(icon!), color: foreground), SizedBox(height: tokens.gapXs + 2), labelText, ], @@ -620,11 +617,7 @@ class SettingsChoiceChip extends StatelessWidget { content = Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon( - context.isMornye ? mornyeIconFor(icon!) : icon, - size: 18, - color: foreground, - ), + Icon(context.adaptiveIcon(icon!), size: 18, color: foreground), SizedBox(width: tokens.gapSm), Flexible(child: labelText), ],