diff --git a/lib/screens/favorite_artists_screen.dart b/lib/screens/favorite_artists_screen.dart index d30e196c..377ccfb3 100644 --- a/lib/screens/favorite_artists_screen.dart +++ b/lib/screens/favorite_artists_screen.dart @@ -5,9 +5,9 @@ import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/library_collections_provider.dart'; import 'package:spotiflac_android/screens/artist_screen.dart'; import 'package:spotiflac_android/services/cover_cache_manager.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/utils/nav_bar_inset.dart'; import 'package:spotiflac_android/widgets/animation_utils.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class FavoriteArtistsScreen extends ConsumerWidget { const FavoriteArtistsScreen({super.key}); @@ -18,49 +18,12 @@ class FavoriteArtistsScreen extends ConsumerWidget { libraryCollectionsProvider.select((state) => state.favoriteArtists), ); final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); final bottomInset = context.navBarBottomInset; return Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only(left: leftPadding, bottom: 16), - title: Text( - context.l10n.collectionFavoriteArtists, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: context.l10n.collectionFavoriteArtists), if (artists.isEmpty) SliverFillRemaining( hasScrollBody: false, diff --git a/lib/screens/settings/about_page.dart b/lib/screens/settings/about_page.dart index 6713c2f4..14b268cf 100644 --- a/lib/screens/settings/about_page.dart +++ b/lib/screens/settings/about_page.dart @@ -4,8 +4,8 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:spotiflac_android/services/cover_cache_manager.dart'; import 'package:spotiflac_android/constants/app_info.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class AboutPage extends StatelessWidget { const AboutPage({super.key}); @@ -13,52 +13,13 @@ class AboutPage extends StatelessWidget { @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: true, child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.aboutTitle, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: context.l10n.aboutTitle), SliverToBoxAdapter( child: Padding( diff --git a/lib/screens/settings/app_settings_page.dart b/lib/screens/settings/app_settings_page.dart index 6394d236..3b37ca0d 100644 --- a/lib/screens/settings/app_settings_page.dart +++ b/lib/screens/settings/app_settings_page.dart @@ -3,8 +3,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/download_queue_provider.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class AppSettingsPage extends ConsumerWidget { const AppSettingsPage({super.key}); @@ -13,52 +13,13 @@ class AppSettingsPage extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final settings = ref.watch(settingsProvider); final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: true, child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.settingsApp, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: context.l10n.settingsApp), SliverToBoxAdapter( child: SettingsSectionHeader(title: context.l10n.sectionApp), diff --git a/lib/screens/settings/appearance_settings_page.dart b/lib/screens/settings/appearance_settings_page.dart index 9e5cc131..fa194ec2 100644 --- a/lib/screens/settings/appearance_settings_page.dart +++ b/lib/screens/settings/appearance_settings_page.dart @@ -4,8 +4,8 @@ import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/l10n/supported_locales.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; import 'package:spotiflac_android/providers/theme_provider.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class AppearanceSettingsPage extends ConsumerWidget { const AppearanceSettingsPage({super.key}); @@ -14,31 +14,13 @@ class AppearanceSettingsPage extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final themeSettings = ref.watch(themeProvider); final settings = ref.watch(settingsProvider); - final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: true, child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: _AppBarTitle( - title: context.l10n.appearanceTitle, - topPadding: topPadding, - ), - ), + SettingsSliverAppBar(title: context.l10n.appearanceTitle), SliverToBoxAdapter( child: Padding( @@ -433,40 +415,6 @@ class _ColorPaletteItem extends StatelessWidget { } } -class _AppBarTitle extends StatelessWidget { - final String title; - final double topPadding; - - const _AppBarTitle({required this.title, required this.topPadding}); - - @override - Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - return LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only(left: leftPadding, bottom: 16), - title: Text( - title, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ); - } -} - class _ThemeModeSelector extends StatelessWidget { final ThemeMode currentMode; final ValueChanged onChanged; diff --git a/lib/screens/settings/backup_restore_page.dart b/lib/screens/settings/backup_restore_page.dart index 3a1dc99b..aaa21a35 100644 --- a/lib/screens/settings/backup_restore_page.dart +++ b/lib/screens/settings/backup_restore_page.dart @@ -11,9 +11,9 @@ import 'package:spotiflac_android/providers/library_collections_provider.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; import 'package:spotiflac_android/services/backup_service.dart'; import 'package:spotiflac_android/services/history_database.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/utils/logger.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class BackupRestorePage extends ConsumerStatefulWidget { const BackupRestorePage({super.key}); @@ -231,50 +231,12 @@ class _BackupRestorePageState extends ConsumerState { @override Widget build(BuildContext context) { - final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); final l10n = context.l10n; return Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only(left: leftPadding, bottom: 16), - title: Text( - l10n.backupTitle, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: l10n.backupTitle), SliverToBoxAdapter( child: SettingsSectionHeader( title: l10n.backupExportSectionTitle, diff --git a/lib/screens/settings/cache_management_page.dart b/lib/screens/settings/cache_management_page.dart index e8b43677..43196fe3 100644 --- a/lib/screens/settings/cache_management_page.dart +++ b/lib/screens/settings/cache_management_page.dart @@ -12,8 +12,9 @@ import 'package:spotiflac_android/providers/local_library_provider.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; import 'package:spotiflac_android/services/cover_cache_manager.dart'; import 'package:spotiflac_android/services/platform_bridge.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; +import 'package:spotiflac_android/utils/string_utils.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class CacheManagementPage extends ConsumerStatefulWidget { const CacheManagementPage({super.key}); @@ -333,21 +334,12 @@ class _CacheManagementPageState extends ConsumerState { }); } - String _formatBytes(int bytes) { - if (bytes < 1024) return '$bytes B'; - if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB'; - if (bytes < 1024 * 1024 * 1024) { - return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; - } - return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB'; - } - String _formatDirectorySize(_DirectoryStats stats) { if (stats.fileCount == 0 || stats.totalSizeBytes == 0) { return context.l10n.cacheNoData; } return context.l10n.cacheSizeWithFiles( - _formatBytes(stats.totalSizeBytes), + formatBytes(stats.totalSizeBytes), stats.fileCount, ); } @@ -374,24 +366,13 @@ class _CacheManagementPageState extends ConsumerState { @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); final overview = _overview; return Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), + SettingsSliverAppBar( + title: context.l10n.cacheTitle, actions: [ IconButton( tooltip: context.l10n.cacheRefresh, @@ -399,30 +380,6 @@ class _CacheManagementPageState extends ConsumerState { icon: const Icon(Icons.refresh), ), ], - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only(left: leftPadding, bottom: 16), - title: Text( - context.l10n.cacheTitle, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), ), if (_isLoading || overview == null) @@ -451,7 +408,7 @@ class _CacheManagementPageState extends ConsumerState { const SizedBox(height: 6), Text( context.l10n.cacheEstimatedTotal( - _formatBytes(overview.totalKnownDiskCacheBytes), + formatBytes(overview.totalKnownDiskCacheBytes), ), style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: colorScheme.onPrimaryContainer, @@ -552,7 +509,7 @@ class _CacheManagementPageState extends ConsumerState { overview.coverStats.fileCount > 0 && overview.coverStats.totalSizeBytes > 0 ? context.l10n.cacheSizeWithFiles( - _formatBytes(overview.coverStats.totalSizeBytes), + formatBytes(overview.coverStats.totalSizeBytes), overview.coverStats.fileCount, ) : context.l10n.cacheNoData, @@ -574,7 +531,7 @@ class _CacheManagementPageState extends ConsumerState { overview.libraryCoverStats.fileCount > 0 && overview.libraryCoverStats.totalSizeBytes > 0 ? context.l10n.cacheSizeWithFiles( - _formatBytes( + formatBytes( overview.libraryCoverStats.totalSizeBytes, ), overview.libraryCoverStats.fileCount, @@ -597,7 +554,7 @@ class _CacheManagementPageState extends ConsumerState { context.l10n.cacheExploreFeedDesc, overview.hasExploreCache ? context.l10n.cacheSizeOnly( - _formatBytes(overview.exploreCacheBytes), + formatBytes(overview.exploreCacheBytes), ) : context.l10n.cacheNoData, ), diff --git a/lib/screens/settings/donate_page.dart b/lib/screens/settings/donate_page.dart index 6aa4ad6c..b79deb5e 100644 --- a/lib/screens/settings/donate_page.dart +++ b/lib/screens/settings/donate_page.dart @@ -4,8 +4,8 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:spotiflac_android/services/app_remote_config_service.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/widgets/donate_icons.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class DonatePage extends StatefulWidget { const DonatePage({super.key}); @@ -56,47 +56,11 @@ class _DonatePageState extends State { @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only(left: leftPadding, bottom: 16), - title: Text( - 'Donate', - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + const SettingsSliverAppBar(title: 'Donate'), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.all(16), diff --git a/lib/screens/settings/download_fallback_extensions_page.dart b/lib/screens/settings/download_fallback_extensions_page.dart index 5805c9d9..67c7d9a2 100644 --- a/lib/screens/settings/download_fallback_extensions_page.dart +++ b/lib/screens/settings/download_fallback_extensions_page.dart @@ -3,8 +3,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/extension_provider.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class DownloadFallbackExtensionsPage extends ConsumerStatefulWidget { const DownloadFallbackExtensionsPage({super.key}); @@ -52,7 +52,6 @@ class _DownloadFallbackExtensionsPageState @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: !_hasChanges, @@ -66,13 +65,8 @@ class _DownloadFallbackExtensionsPageState child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, + SettingsSliverAppBar( + title: context.l10n.extensionsFallbackTitle, leading: IconButton( tooltip: MaterialLocalizations.of(context).backButtonTooltip, icon: const Icon(Icons.arrow_back), @@ -94,32 +88,6 @@ class _DownloadFallbackExtensionsPageState child: Text(context.l10n.dialogSave), ), ], - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.extensionsFallbackTitle, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), ), SliverToBoxAdapter( child: Padding( diff --git a/lib/screens/settings/download_settings_page.dart b/lib/screens/settings/download_settings_page.dart index af6c98e0..d75d9af5 100644 --- a/lib/screens/settings/download_settings_page.dart +++ b/lib/screens/settings/download_settings_page.dart @@ -5,9 +5,9 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; import 'package:spotiflac_android/providers/extension_provider.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/screens/settings/download_fallback_extensions_page.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class DownloadSettingsPage extends ConsumerStatefulWidget { const DownloadSettingsPage({super.key}); @@ -49,53 +49,13 @@ class _DownloadSettingsPageState extends ConsumerState { ) : false; final nativeWorkerAvailable = Platform.isAndroid && hasDownloadExtensions; - final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: true, child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.settingsDownload, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: context.l10n.settingsDownload), SliverToBoxAdapter( child: SettingsSectionHeader(title: context.l10n.sectionService), diff --git a/lib/screens/settings/extension_detail_page.dart b/lib/screens/settings/extension_detail_page.dart index 0d2fb585..7f669858 100644 --- a/lib/screens/settings/extension_detail_page.dart +++ b/lib/screens/settings/extension_detail_page.dart @@ -6,9 +6,9 @@ import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/extension_provider.dart'; import 'package:spotiflac_android/providers/store_provider.dart'; import 'package:spotiflac_android/services/platform_bridge.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/utils/extension_auth_launcher.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class ExtensionDetailPage extends ConsumerStatefulWidget { final String extensionId; @@ -81,7 +81,6 @@ class _ExtensionDetailPageState extends ConsumerState { ); final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); final hasError = extension.status == 'error'; final healthStatus = extState.healthStatuses[extension.id]; @@ -90,45 +89,7 @@ class _ExtensionDetailPageState extends ConsumerState { child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - extension.displayName, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: extension.displayName), SliverToBoxAdapter( child: Padding( diff --git a/lib/screens/settings/extensions_page.dart b/lib/screens/settings/extensions_page.dart index e48958c3..718a2943 100644 --- a/lib/screens/settings/extensions_page.dart +++ b/lib/screens/settings/extensions_page.dart @@ -12,8 +12,8 @@ import 'package:spotiflac_android/screens/settings/download_fallback_extensions_ import 'package:spotiflac_android/screens/settings/extension_detail_page.dart'; import 'package:spotiflac_android/screens/settings/metadata_provider_priority_page.dart'; import 'package:spotiflac_android/screens/settings/provider_priority_page.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class ExtensionsPage extends ConsumerStatefulWidget { const ExtensionsPage({super.key}); @@ -62,52 +62,13 @@ class _ExtensionsPageState extends ConsumerState { Widget build(BuildContext context) { final extState = ref.watch(extensionProvider); final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: true, child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.extensionsTitle, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: context.l10n.extensionsTitle), if (extState.isLoading) const SliverToBoxAdapter( diff --git a/lib/screens/settings/files_settings_page.dart b/lib/screens/settings/files_settings_page.dart index 9e5a7d58..754326a5 100644 --- a/lib/screens/settings/files_settings_page.dart +++ b/lib/screens/settings/files_settings_page.dart @@ -8,9 +8,9 @@ import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/models/settings.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; import 'package:spotiflac_android/services/platform_bridge.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/utils/file_access.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class FilesSettingsPage extends ConsumerStatefulWidget { const FilesSettingsPage({super.key}); @@ -116,52 +116,13 @@ class _FilesSettingsPageState extends ConsumerState { Widget build(BuildContext context) { final settings = ref.watch(settingsProvider); final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: true, child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.settingsFiles, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: context.l10n.settingsFiles), SliverToBoxAdapter( child: SettingsSectionHeader( diff --git a/lib/screens/settings/library_settings_page.dart b/lib/screens/settings/library_settings_page.dart index b541296e..788b1607 100644 --- a/lib/screens/settings/library_settings_page.dart +++ b/lib/screens/settings/library_settings_page.dart @@ -8,8 +8,8 @@ import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; import 'package:spotiflac_android/providers/local_library_provider.dart'; import 'package:spotiflac_android/services/platform_bridge.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class LibrarySettingsPage extends ConsumerStatefulWidget { const LibrarySettingsPage({super.key}); @@ -340,47 +340,11 @@ class _LibrarySettingsPageState extends ConsumerState { final settings = ref.watch(settingsProvider); final libraryState = ref.watch(localLibraryProvider); final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only(left: leftPadding, bottom: 16), - title: Text( - context.l10n.libraryTitle, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: context.l10n.libraryTitle), SliverToBoxAdapter( child: _LibraryHeroCard( diff --git a/lib/screens/settings/log_screen.dart b/lib/screens/settings/log_screen.dart index b51e0861..1a08afde 100644 --- a/lib/screens/settings/log_screen.dart +++ b/lib/screens/settings/log_screen.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:share_plus/share_plus.dart' show ShareParams, SharePlus; import 'package:spotiflac_android/l10n/l10n.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/utils/logger.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; final RegExp _domainPattern = RegExp( r'domain:\s*([^\s,]+)', @@ -132,7 +132,6 @@ class _LogScreenState extends State { @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); final logs = _filteredLogs; return PopScope( @@ -141,18 +140,8 @@ class _LogScreenState extends State { body: CustomScrollView( controller: _scrollController, slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), + SettingsSliverAppBar( + title: context.l10n.logTitle, actions: [ IconButton( icon: Icon( @@ -203,32 +192,6 @@ class _LogScreenState extends State { ], ), ], - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.logTitle, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), ), SliverToBoxAdapter( diff --git a/lib/screens/settings/lyrics_settings_page.dart b/lib/screens/settings/lyrics_settings_page.dart index 034e76c8..75f74f49 100644 --- a/lib/screens/settings/lyrics_settings_page.dart +++ b/lib/screens/settings/lyrics_settings_page.dart @@ -2,9 +2,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/screens/settings/lyrics_provider_priority_page.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class LyricsSettingsPage extends ConsumerWidget { const LyricsSettingsPage({super.key}); @@ -12,53 +12,13 @@ class LyricsSettingsPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final settings = ref.watch(settingsProvider); - final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: true, child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.settingsLyrics, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: context.l10n.settingsLyrics), SliverToBoxAdapter( child: SettingsSectionHeader(title: context.l10n.sectionLyrics), diff --git a/lib/screens/settings/metadata_settings_page.dart b/lib/screens/settings/metadata_settings_page.dart index 234b5b61..637f8c4f 100644 --- a/lib/screens/settings/metadata_settings_page.dart +++ b/lib/screens/settings/metadata_settings_page.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/settings_provider.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; import 'package:spotiflac_android/utils/artist_utils.dart'; import 'package:spotiflac_android/screens/settings/metadata_provider_priority_page.dart'; import 'package:spotiflac_android/widgets/settings_group.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class MetadataSettingsPage extends ConsumerWidget { const MetadataSettingsPage({super.key}); @@ -13,53 +13,13 @@ class MetadataSettingsPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final settings = ref.watch(settingsProvider); - final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: true, child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, - leading: IconButton( - tooltip: MaterialLocalizations.of(context).backButtonTooltip, - icon: const Icon(Icons.arrow_back), - onPressed: () => Navigator.pop(context), - ), - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.settingsMetadata, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), - ), + SettingsSliverAppBar(title: context.l10n.settingsMetadata), SliverToBoxAdapter( child: SettingsSectionHeader(title: context.l10n.sectionDownload), diff --git a/lib/screens/settings/provider_priority_page.dart b/lib/screens/settings/provider_priority_page.dart index 0335d559..864b7fa2 100644 --- a/lib/screens/settings/provider_priority_page.dart +++ b/lib/screens/settings/provider_priority_page.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; import 'package:spotiflac_android/providers/extension_provider.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class ProviderPriorityPage extends ConsumerStatefulWidget { const ProviderPriorityPage({super.key}); @@ -44,7 +44,6 @@ class _ProviderPriorityPageState extends ConsumerState { @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: !_hasChanges, @@ -58,13 +57,8 @@ class _ProviderPriorityPageState extends ConsumerState { child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, + SettingsSliverAppBar( + title: context.l10n.providerPriorityTitle, leading: IconButton( tooltip: MaterialLocalizations.of(context).backButtonTooltip, icon: const Icon(Icons.arrow_back), @@ -86,32 +80,6 @@ class _ProviderPriorityPageState extends ConsumerState { child: Text(context.l10n.dialogSave), ), ], - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - context.l10n.providerPriorityTitle, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), ), SliverToBoxAdapter( diff --git a/lib/widgets/priority_settings_scaffold.dart b/lib/widgets/priority_settings_scaffold.dart index 96cf23bc..56ab99bf 100644 --- a/lib/widgets/priority_settings_scaffold.dart +++ b/lib/widgets/priority_settings_scaffold.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:spotiflac_android/l10n/l10n.dart'; -import 'package:spotiflac_android/utils/app_bar_layout.dart'; +import 'package:spotiflac_android/widgets/settings_sliver_app_bar.dart'; class PrioritySettingsScaffold extends StatelessWidget { final bool hasChanges; @@ -40,7 +40,6 @@ class PrioritySettingsScaffold extends StatelessWidget { @override Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - final topPadding = normalizedHeaderTopPadding(context); return PopScope( canPop: !hasChanges, @@ -54,13 +53,8 @@ class PrioritySettingsScaffold extends StatelessWidget { child: Scaffold( body: CustomScrollView( slivers: [ - SliverAppBar( - expandedHeight: 120 + topPadding, - collapsedHeight: kToolbarHeight, - floating: false, - pinned: true, - backgroundColor: colorScheme.surface, - surfaceTintColor: Colors.transparent, + SettingsSliverAppBar( + title: title, leading: IconButton( tooltip: MaterialLocalizations.of(context).backButtonTooltip, icon: const Icon(Icons.arrow_back), @@ -73,32 +67,6 @@ class PrioritySettingsScaffold extends StatelessWidget { child: Text(saveLabel ?? context.l10n.dialogSave), ), ], - flexibleSpace: LayoutBuilder( - builder: (context, constraints) { - final maxHeight = 120 + topPadding; - final minHeight = kToolbarHeight + topPadding; - final expandRatio = - ((constraints.maxHeight - minHeight) / - (maxHeight - minHeight)) - .clamp(0.0, 1.0); - final leftPadding = 56 - (32 * expandRatio); - return FlexibleSpaceBar( - expandedTitleScale: 1.0, - titlePadding: EdgeInsets.only( - left: leftPadding, - bottom: 16, - ), - title: Text( - title, - style: TextStyle( - fontSize: 20 + (8 * expandRatio), - fontWeight: FontWeight.bold, - color: colorScheme.onSurface, - ), - ), - ); - }, - ), ), SliverToBoxAdapter( child: Padding(