mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-06-29 17:50:00 +02:00
feat(ui): add bottom inset so scrollable content clears the transparent navbar
This commit is contained in:
@@ -14,6 +14,7 @@ import 'package:spotiflac_android/services/platform_bridge.dart';
|
||||
import 'package:spotiflac_android/utils/file_access.dart';
|
||||
import 'package:spotiflac_android/utils/image_cache_utils.dart';
|
||||
import 'package:spotiflac_android/utils/string_utils.dart';
|
||||
import 'package:spotiflac_android/utils/nav_bar_inset.dart';
|
||||
import 'package:spotiflac_android/widgets/track_collection_quick_actions.dart';
|
||||
import 'package:spotiflac_android/widgets/download_service_picker.dart';
|
||||
import 'package:spotiflac_android/widgets/animation_utils.dart';
|
||||
@@ -336,6 +337,7 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final tracks = _tracks ?? [];
|
||||
final pageBackgroundColor = colorScheme.surface;
|
||||
final bottomInset = context.navBarBottomInset;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: pageBackgroundColor,
|
||||
@@ -361,7 +363,7 @@ class _AlbumScreenState extends ConsumerState<AlbumScreen> {
|
||||
if (!_isLoading && _error == null && tracks.isNotEmpty) ...[
|
||||
_buildTrackList(context, colorScheme, tracks),
|
||||
],
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 32)),
|
||||
SliverToBoxAdapter(child: SizedBox(height: 32 + bottomInset)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:spotiflac_android/providers/playback_provider.dart';
|
||||
import 'package:spotiflac_android/services/platform_bridge.dart';
|
||||
import 'package:spotiflac_android/utils/file_access.dart';
|
||||
import 'package:spotiflac_android/utils/string_utils.dart';
|
||||
import 'package:spotiflac_android/utils/nav_bar_inset.dart';
|
||||
import 'package:spotiflac_android/screens/album_screen.dart';
|
||||
import 'package:spotiflac_android/screens/home_tab.dart'
|
||||
show ExtensionAlbumScreen;
|
||||
@@ -456,6 +457,7 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
|
||||
final albumsOnly = _albumsOnlyBucket;
|
||||
final singles = _singlesBucket;
|
||||
final compilations = _compilationsBucket;
|
||||
final bottomInset = context.navBarBottomInset;
|
||||
|
||||
final hasDiscography =
|
||||
!_isLoadingDiscography && _error == null && albums.isNotEmpty;
|
||||
@@ -542,6 +544,7 @@ class _ArtistScreenState extends ConsumerState<ArtistScreen> {
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(height: _isSelectionMode ? 120 : 32),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: bottomInset)),
|
||||
],
|
||||
),
|
||||
if (_isSelectionMode)
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:spotiflac_android/utils/audio_conversion_utils.dart';
|
||||
import 'package:spotiflac_android/utils/file_access.dart';
|
||||
import 'package:spotiflac_android/utils/image_cache_utils.dart';
|
||||
import 'package:spotiflac_android/utils/lyrics_metadata_helper.dart';
|
||||
import 'package:spotiflac_android/utils/nav_bar_inset.dart';
|
||||
import 'package:spotiflac_android/providers/download_queue_provider.dart';
|
||||
import 'package:spotiflac_android/widgets/batch_progress_dialog.dart';
|
||||
import 'package:spotiflac_android/widgets/batch_convert_sheet.dart';
|
||||
@@ -358,6 +359,7 @@ class _DownloadedAlbumScreenState extends ConsumerState<DownloadedAlbumScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final bottomPadding = MediaQuery.of(context).padding.bottom;
|
||||
final bottomInset = context.navBarBottomInset;
|
||||
|
||||
final tracksValue = ref.watch(
|
||||
downloadedAlbumTracksProvider(
|
||||
@@ -413,6 +415,7 @@ class _DownloadedAlbumScreenState extends ConsumerState<DownloadedAlbumScreen> {
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(height: _isSelectionMode ? 120 : 32),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: bottomInset)),
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ 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';
|
||||
|
||||
class FavoriteArtistsScreen extends ConsumerWidget {
|
||||
@@ -18,6 +19,7 @@ class FavoriteArtistsScreen extends ConsumerWidget {
|
||||
);
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final topPadding = normalizedHeaderTopPadding(context);
|
||||
final bottomInset = context.navBarBottomInset;
|
||||
|
||||
return Scaffold(
|
||||
body: CustomScrollView(
|
||||
@@ -155,6 +157,7 @@ class FavoriteArtistsScreen extends ConsumerWidget {
|
||||
);
|
||||
},
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: bottomInset)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -21,6 +21,7 @@ import 'package:spotiflac_android/services/csv_import_service.dart';
|
||||
import 'package:spotiflac_android/services/downloaded_embedded_cover_resolver.dart';
|
||||
import 'package:spotiflac_android/services/platform_bridge.dart';
|
||||
import 'package:spotiflac_android/utils/app_bar_layout.dart';
|
||||
import 'package:spotiflac_android/utils/nav_bar_inset.dart';
|
||||
import 'package:spotiflac_android/utils/file_access.dart';
|
||||
import 'package:spotiflac_android/utils/string_utils.dart';
|
||||
import 'package:spotiflac_android/screens/playlist_screen.dart';
|
||||
@@ -1205,6 +1206,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
final screenHeight = mediaQuery.size.height;
|
||||
final topPadding = normalizedHeaderTopPadding(context);
|
||||
final bottomInset = context.navBarBottomInset;
|
||||
final hasHistoryItems = ref.watch(
|
||||
_homeHistoryPreviewProvider.select((items) => items.isNotEmpty),
|
||||
);
|
||||
@@ -1540,6 +1542,7 @@ class _HomeTabState extends ConsumerState<HomeTab>
|
||||
);
|
||||
},
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: bottomInset)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -10,6 +10,7 @@ import 'package:spotiflac_android/screens/library_tracks_folder_screen.dart';
|
||||
import 'package:spotiflac_android/services/cover_cache_manager.dart';
|
||||
import 'package:spotiflac_android/widgets/bottom_sheet_option_tile.dart';
|
||||
import 'package:spotiflac_android/utils/app_bar_layout.dart';
|
||||
import 'package:spotiflac_android/utils/nav_bar_inset.dart';
|
||||
|
||||
class LibraryPlaylistsScreen extends ConsumerWidget {
|
||||
const LibraryPlaylistsScreen({super.key});
|
||||
@@ -21,6 +22,7 @@ class LibraryPlaylistsScreen extends ConsumerWidget {
|
||||
);
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final topPadding = normalizedHeaderTopPadding(context);
|
||||
final bottomInset = context.navBarBottomInset;
|
||||
|
||||
return Scaffold(
|
||||
body: CustomScrollView(
|
||||
@@ -132,6 +134,7 @@ class LibraryPlaylistsScreen extends ConsumerWidget {
|
||||
);
|
||||
}, childCount: playlists.length * 2 - 1),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: bottomInset)),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
|
||||
@@ -14,6 +14,7 @@ import 'package:spotiflac_android/providers/playback_provider.dart';
|
||||
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/utils/nav_bar_inset.dart';
|
||||
import 'package:spotiflac_android/screens/track_metadata_screen.dart';
|
||||
import 'package:spotiflac_android/widgets/download_service_picker.dart';
|
||||
import 'package:spotiflac_android/widgets/playlist_picker_sheet.dart';
|
||||
@@ -322,6 +323,7 @@ class _LibraryTracksFolderScreenState
|
||||
.maybeWhen(data: (keys) => keys, orElse: () => const <String>{});
|
||||
|
||||
final bottomPadding = MediaQuery.of(context).padding.bottom;
|
||||
final bottomInset = context.navBarBottomInset;
|
||||
|
||||
return PopScope(
|
||||
canPop: !_isSelectionMode,
|
||||
@@ -379,6 +381,7 @@ class _LibraryTracksFolderScreenState
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(height: _isSelectionMode ? 200 : 32),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: bottomInset)),
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import 'package:spotiflac_android/utils/audio_conversion_utils.dart';
|
||||
import 'package:spotiflac_android/utils/file_access.dart';
|
||||
import 'package:spotiflac_android/utils/image_cache_utils.dart';
|
||||
import 'package:spotiflac_android/utils/lyrics_metadata_helper.dart';
|
||||
import 'package:spotiflac_android/utils/nav_bar_inset.dart';
|
||||
import 'package:spotiflac_android/services/library_database.dart';
|
||||
import 'package:spotiflac_android/services/ffmpeg_service.dart';
|
||||
import 'package:spotiflac_android/services/replaygain_service.dart';
|
||||
@@ -252,6 +253,7 @@ class _LocalAlbumScreenState extends ConsumerState<LocalAlbumScreen> {
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final bottomPadding = MediaQuery.of(context).padding.bottom;
|
||||
final bottomInset = context.navBarBottomInset;
|
||||
final tracks = _sortedTracksCache;
|
||||
|
||||
if (tracks.isEmpty) {
|
||||
@@ -288,6 +290,7 @@ class _LocalAlbumScreenState extends ConsumerState<LocalAlbumScreen> {
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(height: _isSelectionMode ? 120 : 32),
|
||||
),
|
||||
SliverToBoxAdapter(child: SizedBox(height: bottomInset)),
|
||||
],
|
||||
),
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import 'package:spotiflac_android/providers/library_collections_provider.dart';
|
||||
import 'package:spotiflac_android/utils/file_access.dart';
|
||||
import 'package:spotiflac_android/utils/image_cache_utils.dart';
|
||||
import 'package:spotiflac_android/utils/string_utils.dart';
|
||||
import 'package:spotiflac_android/utils/nav_bar_inset.dart';
|
||||
import 'package:spotiflac_android/providers/settings_provider.dart';
|
||||
import 'package:spotiflac_android/providers/local_library_provider.dart';
|
||||
import 'package:spotiflac_android/providers/playback_provider.dart';
|
||||
@@ -253,7 +254,9 @@ class _PlaylistScreenState extends ConsumerState<PlaylistScreen> {
|
||||
_buildAppBar(context, colorScheme),
|
||||
_buildInfoCard(context, colorScheme),
|
||||
_buildTrackList(context, colorScheme),
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 32)),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(height: 32 + context.navBarBottomInset),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@ import 'package:spotiflac_android/widgets/settings_group.dart';
|
||||
import 'package:spotiflac_android/widgets/animation_utils.dart';
|
||||
import 'package:spotiflac_android/screens/store/extension_details_screen.dart';
|
||||
import 'package:spotiflac_android/utils/app_bar_layout.dart';
|
||||
import 'package:spotiflac_android/utils/nav_bar_inset.dart';
|
||||
|
||||
class RepoTab extends ConsumerStatefulWidget {
|
||||
const RepoTab({super.key});
|
||||
@@ -76,6 +77,7 @@ class _RepoTabState extends ConsumerState<RepoTab> {
|
||||
}
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final topPadding = normalizedHeaderTopPadding(context);
|
||||
final bottomInset = context.navBarBottomInset;
|
||||
|
||||
return Scaffold(
|
||||
body: RefreshIndicator(
|
||||
@@ -311,6 +313,7 @@ class _RepoTabState extends ConsumerState<RepoTab> {
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 16)),
|
||||
],
|
||||
],
|
||||
SliverToBoxAdapter(child: SizedBox(height: bottomInset)),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:spotiflac_android/screens/settings/cache_management_page.dart';
|
||||
import 'package:spotiflac_android/screens/settings/donate_page.dart';
|
||||
import 'package:spotiflac_android/screens/settings/log_screen.dart';
|
||||
import 'package:spotiflac_android/utils/app_bar_layout.dart';
|
||||
import 'package:spotiflac_android/utils/nav_bar_inset.dart';
|
||||
import 'package:spotiflac_android/widgets/settings_group.dart';
|
||||
import 'package:spotiflac_android/widgets/animation_utils.dart';
|
||||
|
||||
@@ -25,6 +26,7 @@ class SettingsTab extends ConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final topPadding = normalizedHeaderTopPadding(context);
|
||||
final bottomInset = context.navBarBottomInset;
|
||||
|
||||
return CustomScrollView(
|
||||
slivers: [
|
||||
@@ -183,6 +185,7 @@ class SettingsTab extends ConsumerWidget {
|
||||
),
|
||||
),
|
||||
|
||||
SliverToBoxAdapter(child: SizedBox(height: bottomInset)),
|
||||
const SliverFillRemaining(hasScrollBody: false, child: SizedBox()),
|
||||
],
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:spotiflac_android/l10n/l10n.dart';
|
||||
import 'package:spotiflac_android/providers/store_provider.dart';
|
||||
import 'package:spotiflac_android/providers/extension_provider.dart';
|
||||
import 'package:spotiflac_android/utils/nav_bar_inset.dart';
|
||||
|
||||
class ExtensionDetailsScreen extends ConsumerStatefulWidget {
|
||||
final StoreExtension extension;
|
||||
@@ -69,7 +70,9 @@ class _ExtensionDetailsScreenState
|
||||
),
|
||||
_buildCapabilities(context, liveExtension, colorScheme),
|
||||
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 32)),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(height: 32 + context.navBarBottomInset),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
/// Bottom inset needed to clear the transparent shell navigation bar.
|
||||
///
|
||||
/// The shell Scaffold uses `extendBody: true`, so its body (and any route
|
||||
/// pushed inside the tab navigators) receives the navbar height plus the system
|
||||
/// gesture inset as `MediaQuery.padding.bottom`. Scrollable screens add this as
|
||||
/// trailing padding so their last item can scroll clear of the bar while the
|
||||
/// content still shows faintly behind it.
|
||||
extension NavBarInset on BuildContext {
|
||||
double get navBarBottomInset => MediaQuery.paddingOf(this).bottom;
|
||||
}
|
||||
Reference in New Issue
Block a user