mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-12 23:45:18 +00:00
fix(android): back button and safe area issues
This commit is contained in:
parent
6ddf6b9cce
commit
d4504722d8
@ -1,3 +1,4 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
||||
@ -73,6 +74,10 @@ class TitleBar extends HookConsumerWidget implements PreferredSizeWidget {
|
||||
final hasFullscreen =
|
||||
MediaQuery.sizeOf(context).width == constraints.maxWidth;
|
||||
|
||||
final canPop = leading.isEmpty &&
|
||||
automaticallyImplyLeading &&
|
||||
(Navigator.canPop(context) || context.watchRouter.canPop());
|
||||
|
||||
return GestureDetector(
|
||||
onHorizontalDragStart: (_) => onDrag(ref),
|
||||
onVerticalDragStart: (_) => onDrag(ref),
|
||||
@ -94,13 +99,7 @@ class TitleBar extends HookConsumerWidget implements PreferredSizeWidget {
|
||||
}
|
||||
},
|
||||
child: AppBar(
|
||||
leading: leading.isEmpty &&
|
||||
automaticallyImplyLeading &&
|
||||
Navigator.canPop(context)
|
||||
? [
|
||||
const BackButton(),
|
||||
]
|
||||
: leading,
|
||||
leading: canPop ? [const BackButton()] : leading,
|
||||
trailing: [
|
||||
...trailing,
|
||||
Align(
|
||||
|
@ -23,12 +23,11 @@ class ConnectPage extends HookConsumerWidget {
|
||||
final connectClientsNotifier = ref.read(connectClientsProvider.notifier);
|
||||
final discoveredDevices = connectClients.asData?.value.services;
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
automaticallyImplyLeading: true,
|
||||
title: Text(context.l10n.devices),
|
||||
)
|
||||
TitleBar(title: Text(context.l10n.devices)),
|
||||
],
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
@ -84,6 +83,7 @@ class ConnectPage extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,6 @@ class ConnectControlPage extends HookConsumerWidget {
|
||||
headers: [
|
||||
TitleBar(
|
||||
title: Text(resolvedService!.name),
|
||||
automaticallyImplyLeading: true,
|
||||
)
|
||||
],
|
||||
child: LayoutBuilder(builder: (context, constrains) {
|
||||
|
@ -28,13 +28,14 @@ class HomeFeedSectionPage extends HookConsumerWidget {
|
||||
final controller = useScrollController();
|
||||
final isArtist = section.items.every((item) => item.artist != null);
|
||||
|
||||
return Skeletonizer(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Skeletonizer(
|
||||
enabled: homeFeedSection.isLoading,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
title: Text(section.title ?? ""),
|
||||
automaticallyImplyLeading: true,
|
||||
)
|
||||
],
|
||||
child: Padding(
|
||||
@ -44,7 +45,8 @@ class HomeFeedSectionPage extends HookConsumerWidget {
|
||||
slivers: [
|
||||
if (isArtist)
|
||||
SliverGrid.builder(
|
||||
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
gridDelegate:
|
||||
const SliverGridDelegateWithMaxCrossAxisExtent(
|
||||
maxCrossAxisExtent: 200,
|
||||
mainAxisExtent: 250,
|
||||
crossAxisSpacing: 8,
|
||||
@ -93,6 +95,7 @@ class HomeFeedSectionPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,8 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
automaticSystemUiAdjustment: false,
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
if (kIsDesktop)
|
||||
const TitleBar(
|
||||
@ -74,7 +75,9 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
child: CustomScrollView(
|
||||
controller: scrollController,
|
||||
slivers: [
|
||||
SliverAppBar(
|
||||
SliverSafeArea(
|
||||
bottom: false,
|
||||
sliver: SliverAppBar(
|
||||
automaticallyImplyLeading: false,
|
||||
leading: kIsMobile ? const BackButton() : null,
|
||||
expandedHeight: mediaQuery.mdAndDown ? 200 : 150,
|
||||
@ -110,6 +113,7 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
collapseMode: CollapseMode.parallax,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SliverGap(20),
|
||||
SliverSafeArea(
|
||||
top: false,
|
||||
@ -123,8 +127,8 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
isLoading: playlists.isLoading,
|
||||
hasMore: playlists.asData?.value.hasMore == true,
|
||||
onRequestMore: playlistsNotifier.fetchMore,
|
||||
listItemBuilder: (context, index) =>
|
||||
PlaylistCard.tile(playlists.asData!.value.items[index]),
|
||||
listItemBuilder: (context, index) => PlaylistCard.tile(
|
||||
playlists.asData!.value.items[index]),
|
||||
gridItemBuilder: (context, index) =>
|
||||
PlaylistCard(playlists.asData!.value.items[index]),
|
||||
),
|
||||
@ -135,6 +139,7 @@ class GenrePlaylistsPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ class GenrePage extends HookConsumerWidget {
|
||||
headers: [
|
||||
TitleBar(
|
||||
title: Text(context.l10n.explore_genres),
|
||||
automaticallyImplyLeading: true,
|
||||
)
|
||||
],
|
||||
child: GridView.builder(
|
||||
|
@ -31,6 +31,7 @@ class LastFMLoginPage extends HookConsumerWidget {
|
||||
return Scaffold(
|
||||
headers: const [
|
||||
SafeArea(
|
||||
bottom: false,
|
||||
child: TitleBar(
|
||||
leading: [BackButton()],
|
||||
),
|
||||
@ -39,7 +40,8 @@ class LastFMLoginPage extends HookConsumerWidget {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
Flexible(
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 400),
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.all(16),
|
||||
@ -139,6 +141,7 @@ class LastFMLoginPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -256,7 +256,9 @@ class PlaylistGeneratorPage extends HookConsumerWidget {
|
||||
|
||||
final controller = useScrollController();
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
leading: const [BackButton()],
|
||||
@ -308,8 +310,8 @@ class PlaylistGeneratorPage extends HookConsumerWidget {
|
||||
),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
value:
|
||||
SliderValue.single(value.toDouble()),
|
||||
value: SliderValue.single(
|
||||
value.toDouble()),
|
||||
min: 10,
|
||||
max: 100,
|
||||
divisions: 9,
|
||||
@ -655,8 +657,9 @@ class PlaylistGeneratorPage extends HookConsumerWidget {
|
||||
seedArtists: artists.value
|
||||
.map((a) => a.id!)
|
||||
.toList(),
|
||||
seedTracks:
|
||||
tracks.value.map((t) => t.id!).toList(),
|
||||
seedTracks: tracks.value
|
||||
.map((t) => t.id!)
|
||||
.toList(),
|
||||
seedGenres: genres.value,
|
||||
limit: limit.value,
|
||||
max: max.value,
|
||||
@ -680,6 +683,7 @@ class PlaylistGeneratorPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,9 @@ class PlaylistGenerateResultPage extends HookConsumerWidget {
|
||||
final isAllTrackSelected = selectedTracks.value.length ==
|
||||
(generatedPlaylist.asData?.value.length ?? 0);
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: const [
|
||||
TitleBar(leading: [BackButton()])
|
||||
],
|
||||
@ -101,7 +103,8 @@ class PlaylistGenerateResultPage extends HookConsumerWidget {
|
||||
: () async {
|
||||
await playlistNotifier.addTracks(
|
||||
generatedPlaylist.asData!.value.where(
|
||||
(e) => selectedTracks.value.contains(e.id!),
|
||||
(e) =>
|
||||
selectedTracks.value.contains(e.id!),
|
||||
),
|
||||
);
|
||||
if (context.mounted) {
|
||||
@ -152,11 +155,13 @@ class PlaylistGenerateResultPage extends HookConsumerWidget {
|
||||
: () async {
|
||||
final hasAdded = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) => PlaylistAddTrackDialog(
|
||||
builder: (context) =>
|
||||
PlaylistAddTrackDialog(
|
||||
openFromPlaylist: null,
|
||||
tracks: selectedTracks.value
|
||||
.map(
|
||||
(e) => generatedPlaylist.asData!.value
|
||||
(e) => generatedPlaylist
|
||||
.asData!.value
|
||||
.firstWhere(
|
||||
(element) => element.id == e,
|
||||
),
|
||||
@ -244,7 +249,8 @@ class PlaylistGenerateResultPage extends HookConsumerWidget {
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
selectedTracks.value.contains(track.id)
|
||||
? selectedTracks.value.remove(track.id)
|
||||
? selectedTracks.value
|
||||
.remove(track.id)
|
||||
: selectedTracks.value.add(track.id!);
|
||||
selectedTracks.value =
|
||||
selectedTracks.value.toList();
|
||||
@ -260,6 +266,7 @@ class PlaylistGenerateResultPage extends HookConsumerWidget {
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,9 @@ class WebViewLoginPage extends HookConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: const [
|
||||
TitleBar(
|
||||
leading: [BackButton(color: Colors.white)],
|
||||
@ -72,6 +74,7 @@ class WebViewLoginPage extends HookConsumerWidget {
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,6 @@ class ProfilePage extends HookConsumerWidget {
|
||||
headers: [
|
||||
TitleBar(
|
||||
title: Text(context.l10n.profile),
|
||||
automaticallyImplyLeading: true,
|
||||
)
|
||||
],
|
||||
child: Skeletonizer(
|
||||
|
@ -31,7 +31,9 @@ class AboutSpotubePage extends HookConsumerWidget {
|
||||
|
||||
const colon = TableCell(child: Text(":"));
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
leading: const [BackButton()],
|
||||
@ -91,8 +93,8 @@ class AboutSpotubePage extends HookConsumerWidget {
|
||||
TableCell(child: Text(context.l10n.build_number)),
|
||||
colon,
|
||||
TableCell(
|
||||
child: Text(
|
||||
packageInfo.buildNumber.replaceAll(".", " ")),
|
||||
child: Text(packageInfo.buildNumber
|
||||
.replaceAll(".", " ")),
|
||||
)
|
||||
],
|
||||
),
|
||||
@ -191,6 +193,7 @@ class AboutSpotubePage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,9 @@ class BlackListPage extends HookConsumerWidget {
|
||||
[blacklist, searchText.value],
|
||||
);
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
title: Text(context.l10n.blacklist),
|
||||
@ -81,9 +83,8 @@ class BlackListPage extends HookConsumerWidget {
|
||||
trailing: IconButton.ghost(
|
||||
icon: Icon(SpotubeIcons.trash, color: Colors.red[400]),
|
||||
onPressed: () {
|
||||
ref
|
||||
.read(blacklistProvider.notifier)
|
||||
.remove(filteredBlacklist.elementAt(index).elementId);
|
||||
ref.read(blacklistProvider.notifier).remove(
|
||||
filteredBlacklist.elementAt(index).elementId);
|
||||
},
|
||||
),
|
||||
);
|
||||
@ -92,6 +93,7 @@ class BlackListPage extends HookConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ class SettingsPage extends HookConsumerWidget {
|
||||
headers: [
|
||||
TitleBar(
|
||||
title: Text(context.l10n.settings),
|
||||
automaticallyImplyLeading: true,
|
||||
)
|
||||
],
|
||||
child: Scrollbar(
|
||||
|
@ -26,10 +26,11 @@ class StatsAlbumsPage extends HookConsumerWidget {
|
||||
|
||||
final albumsData = topAlbums.asData?.value.items ?? [];
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
automaticallyImplyLeading: true,
|
||||
title: Text(context.l10n.albums),
|
||||
)
|
||||
],
|
||||
@ -53,6 +54,7 @@ class StatsAlbumsPage extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -29,10 +29,11 @@ class StatsArtistsPage extends HookConsumerWidget {
|
||||
final artistsData = useMemoized(
|
||||
() => topTracks.asData?.value.artists ?? [], [topTracks.asData?.value]);
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
automaticallyImplyLeading: true,
|
||||
title: Text(context.l10n.artists),
|
||||
)
|
||||
],
|
||||
@ -56,6 +57,7 @@ class StatsArtistsPage extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -50,10 +50,11 @@ class StatsStreamFeesPage extends HookConsumerWidget {
|
||||
HistoryDuration.allTime: context.l10n.all_time,
|
||||
};
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
automaticallyImplyLeading: true,
|
||||
title: Text(context.l10n.streaming_fees_hypothetical),
|
||||
)
|
||||
],
|
||||
@ -86,7 +87,8 @@ class StatsStreamFeesPage extends HookConsumerWidget {
|
||||
if (value == null) return;
|
||||
duration.value = value;
|
||||
},
|
||||
itemBuilder: (context, value) => Text(translations[value]!),
|
||||
itemBuilder: (context, value) =>
|
||||
Text(translations[value]!),
|
||||
constraints: const BoxConstraints(maxWidth: 150),
|
||||
popupWidthConstraint: PopoverConstraint.anchorMaxSize,
|
||||
children: [
|
||||
@ -109,7 +111,8 @@ class StatsStreamFeesPage extends HookConsumerWidget {
|
||||
await topTracksNotifier.fetchMore();
|
||||
},
|
||||
hasError: topTracks.hasError,
|
||||
isLoading: topTracks.isLoading && !topTracks.isLoadingNextPage,
|
||||
isLoading:
|
||||
topTracks.isLoading && !topTracks.isLoadingNextPage,
|
||||
hasReachedMax: topTracks.asData?.value.hasMore ?? true,
|
||||
itemCount: artistsData.length,
|
||||
itemBuilder: (context, index) {
|
||||
@ -124,6 +127,7 @@ class StatsStreamFeesPage extends HookConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -28,11 +28,12 @@ class StatsMinutesPage extends HookConsumerWidget {
|
||||
|
||||
final tracksData = topTracks.asData?.value.items ?? [];
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
title: Text(context.l10n.minutes_listened),
|
||||
automaticallyImplyLeading: true,
|
||||
)
|
||||
],
|
||||
child: Skeletonizer(
|
||||
@ -58,6 +59,7 @@ class StatsMinutesPage extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -27,10 +27,11 @@ class StatsPlaylistsPage extends HookConsumerWidget {
|
||||
|
||||
final playlistsData = topPlaylists.asData?.value.items ?? [];
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
automaticallyImplyLeading: true,
|
||||
title: Text(context.l10n.playlists),
|
||||
)
|
||||
],
|
||||
@ -41,7 +42,8 @@ class StatsPlaylistsPage extends HookConsumerWidget {
|
||||
await topPlaylistsNotifier.fetchMore();
|
||||
},
|
||||
hasError: topPlaylists.hasError,
|
||||
isLoading: topPlaylists.isLoading && !topPlaylists.isLoadingNextPage,
|
||||
isLoading:
|
||||
topPlaylists.isLoading && !topPlaylists.isLoadingNextPage,
|
||||
hasReachedMax: topPlaylists.asData?.value.hasMore ?? true,
|
||||
itemCount: playlistsData.length,
|
||||
itemBuilder: (context, index) {
|
||||
@ -49,13 +51,14 @@ class StatsPlaylistsPage extends HookConsumerWidget {
|
||||
return StatsPlaylistItem(
|
||||
playlist: playlist.playlist,
|
||||
info: Text(
|
||||
context.l10n
|
||||
.count_plays(compactNumberFormatter.format(playlist.count)),
|
||||
context.l10n.count_plays(
|
||||
compactNumberFormatter.format(playlist.count)),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -28,11 +28,12 @@ class StatsStreamsPage extends HookConsumerWidget {
|
||||
|
||||
final tracksData = topTracks.asData?.value.items ?? [];
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: [
|
||||
TitleBar(
|
||||
title: Text(context.l10n.streamed_songs),
|
||||
automaticallyImplyLeading: true,
|
||||
)
|
||||
],
|
||||
child: Skeletonizer(
|
||||
@ -58,6 +59,7 @@ class StatsStreamsPage extends HookConsumerWidget {
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -54,10 +54,11 @@ class TrackPage extends HookConsumerWidget {
|
||||
}
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
return SafeArea(
|
||||
bottom: false,
|
||||
child: Scaffold(
|
||||
headers: const [
|
||||
TitleBar(
|
||||
automaticallyImplyLeading: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
surfaceBlur: 0,
|
||||
)
|
||||
@ -121,7 +122,8 @@ class TrackPage extends HookConsumerWidget {
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: mediaQuery.smAndDown
|
||||
? CrossAxisAlignment.center
|
||||
@ -246,6 +248,7 @@ class TrackPage extends HookConsumerWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user