fix: SafeArea not working for bottom floating widgets

This commit is contained in:
Kingkor Roy Tirtho 2024-12-29 13:36:44 +06:00
parent b558cc17f1
commit 047eccfa82
12 changed files with 153 additions and 169 deletions

View File

@ -51,6 +51,8 @@ class PlaybuttonView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final scale = context.theme.scaling;
return SliverLayoutBuilder( return SliverLayoutBuilder(
builder: (context, constrains) => HookBuilder(builder: (context) { builder: (context, constrains) => HookBuilder(builder: (context) {
final isGrid = useState(constrains.mdAndUp); final isGrid = useState(constrains.mdAndUp);
@ -100,10 +102,10 @@ class PlaybuttonView extends StatelessWidget {
(true, _) => SliverGrid.builder( (true, _) => SliverGrid.builder(
itemCount: isLoading ? 6 : itemCount + 1, itemCount: isLoading ? 6 : itemCount + 1,
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150 * context.theme.scaling, maxCrossAxisExtent: 150 * scale,
mainAxisExtent: 225 * context.theme.scaling, mainAxisExtent: 225 * scale,
crossAxisSpacing: 8, crossAxisSpacing: 12 * scale,
mainAxisSpacing: 8, mainAxisSpacing: 12 * scale,
), ),
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (isLoading) { if (isLoading) {

View File

@ -48,6 +48,7 @@ class UserAlbums extends HookConsumerWidget {
} }
return SafeArea( return SafeArea(
bottom: false,
child: Scaffold( child: Scaffold(
child: RefreshTrigger( child: RefreshTrigger(
// onRefresh: () async { // onRefresh: () async {
@ -89,6 +90,7 @@ class UserAlbums extends HookConsumerWidget {
AlbumCard.tile(albums[index]), AlbumCard.tile(albums[index]),
), ),
), ),
const SliverSafeArea(sliver: SliverGap(10)),
], ],
), ),
), ),

View File

@ -53,6 +53,7 @@ class UserArtists extends HookConsumerWidget {
} }
return SafeArea( return SafeArea(
bottom: false,
child: Scaffold( child: Scaffold(
child: RefreshTrigger( child: RefreshTrigger(
// onRefresh: () async { // onRefresh: () async {
@ -117,6 +118,7 @@ class UserArtists extends HookConsumerWidget {
}, },
); );
}), }),
const SliverSafeArea(sliver: SliverGap(10)),
], ],
), ),
), ),

View File

@ -82,6 +82,7 @@ class UserPlaylists extends HookConsumerWidget {
// ref.invalidate(favoritePlaylistsProvider); // ref.invalidate(favoritePlaylistsProvider);
// }, // },
child: SafeArea( child: SafeArea(
bottom: false,
child: InterScrollbar( child: InterScrollbar(
controller: controller, controller: controller,
child: CustomScrollView( child: CustomScrollView(
@ -139,6 +140,7 @@ class UserPlaylists extends HookConsumerWidget {
}, },
), ),
), ),
const SliverSafeArea(sliver: SliverGap(10)),
], ],
), ),
), ),

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
@ -32,7 +33,7 @@ class ZoomControls extends HookWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final actions = [ final actions = [
IconButton( IconButton.ghost(
icon: decreaseIcon, icon: decreaseIcon,
onPressed: () { onPressed: () {
if (value == min) return; if (value == min) return;
@ -40,7 +41,7 @@ class ZoomControls extends HookWidget {
}, },
), ),
Text("$value$unit"), Text("$value$unit"),
IconButton( IconButton.ghost(
icon: increaseIcon, icon: increaseIcon,
onPressed: () { onPressed: () {
if (value == max) return; if (value == max) return;
@ -50,27 +51,28 @@ class ZoomControls extends HookWidget {
]; ];
return Container( return Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor.withOpacity(0.7),
borderRadius: BorderRadius.circular(10),
),
constraints: BoxConstraints( constraints: BoxConstraints(
maxHeight: direction == Axis.horizontal ? 50 : 200, maxHeight: direction == Axis.horizontal ? 50 : 200,
maxWidth: direction == Axis.vertical ? 50 : double.infinity, maxWidth: direction == Axis.vertical ? 50 : double.infinity,
), ),
margin: const EdgeInsets.all(8), margin: const EdgeInsets.all(8),
child: direction == Axis.horizontal child: SurfaceCard(
? Row( surfaceBlur: context.theme.surfaceBlur,
mainAxisAlignment: MainAxisAlignment.end, surfaceOpacity: context.theme.surfaceOpacity,
mainAxisSize: MainAxisSize.min, padding: EdgeInsets.zero,
children: actions, child: direction == Axis.horizontal
) ? Row(
: Column( mainAxisAlignment: MainAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.min,
mainAxisSize: MainAxisSize.min, children: actions,
verticalDirection: VerticalDirection.up, )
children: actions, : Column(
), mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
verticalDirection: VerticalDirection.up,
children: actions,
),
),
); );
} }
} }

View File

@ -367,12 +367,6 @@ class PlayerView extends HookConsumerWidget {
topRight: Radius.circular(20), topRight: Radius.circular(20),
), ),
), ),
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context)
.size
.height *
0.8,
),
builder: (context) => builder: (context) =>
const LyricsPage(isModal: true), const LyricsPage(isModal: true),
); );

View File

@ -1,13 +1,11 @@
import 'dart:ui';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/components/titlebar/titlebar.dart'; import 'package:spotube/components/titlebar/titlebar.dart';
import 'package:spotube/components/image/universal_image.dart'; import 'package:spotube/components/image/universal_image.dart';
import 'package:spotube/extensions/constrains.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/extensions/image.dart'; import 'package:spotube/extensions/image.dart';
import 'package:spotube/hooks/utils/use_custom_status_bar_color.dart'; import 'package:spotube/hooks/utils/use_custom_status_bar_color.dart';
@ -35,7 +33,6 @@ class LyricsPage extends HookConsumerWidget {
[playlist.activeTrack?.album?.images], [playlist.activeTrack?.album?.images],
); );
final palette = usePaletteColor(albumArt, ref); final palette = usePaletteColor(albumArt, ref);
final mediaQuery = MediaQuery.of(context);
final route = ModalRoute.of(context); final route = ModalRoute.of(context);
final selectedIndex = useState(0); final selectedIndex = useState(0);
@ -47,17 +44,28 @@ class LyricsPage extends HookConsumerWidget {
Widget tabbar = Padding( Widget tabbar = Padding(
padding: const EdgeInsets.all(10), padding: const EdgeInsets.all(10),
child: Opacity( child: isModal
opacity: 0.8, ? TabList(
child: Tabs( index: selectedIndex.value,
index: selectedIndex.value, children: [
onChanged: (index) => selectedIndex.value = index, TabButton(
tabs: [ onPressed: () => selectedIndex.value = 0,
Text(context.l10n.synced), child: Text(context.l10n.synced),
Text(context.l10n.plain), ),
], TabButton(
), onPressed: () => selectedIndex.value = 1,
), child: Text(context.l10n.plain),
),
],
)
: Tabs(
index: selectedIndex.value,
onChanged: (index) => selectedIndex.value = index,
tabs: [
Text(context.l10n.synced),
Text(context.l10n.plain),
],
),
); );
tabbar = Row( tabbar = Row(
@ -89,57 +97,53 @@ class LyricsPage extends HookConsumerWidget {
canPop: true, canPop: true,
onPopInvokedWithResult: (_, __) => resetStatusBar(), onPopInvokedWithResult: (_, __) => resetStatusBar(),
child: SafeArea( child: SafeArea(
child: BackdropFilter( bottom: false,
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15), child: SurfaceCard(
child: Container( surfaceBlur: context.theme.surfaceBlur,
clipBehavior: Clip.hardEdge, surfaceOpacity: context.theme.surfaceOpacity,
decoration: BoxDecoration( padding: EdgeInsets.zero,
color: Theme.of(context).colorScheme.background.withOpacity(.4), borderRadius: BorderRadius.zero,
borderRadius: const BorderRadius.only( borderWidth: 0,
topLeft: Radius.circular(10), child: Column(
topRight: Radius.circular(10), children: [
), const SizedBox(height: 5),
), Container(
child: Column( height: 7,
children: [ width: 150,
const SizedBox(height: 5), decoration: BoxDecoration(
Container( color: palette.titleTextColor,
height: 7, borderRadius: BorderRadius.circular(10),
width: 150,
decoration: BoxDecoration(
color: palette.titleTextColor,
borderRadius: BorderRadius.circular(10),
),
), ),
AppBar( ),
leading: [tabbar], Row(
backgroundColor: Colors.transparent, children: [
trailing: [ Expanded(
IconButton.ghost( child: tabbar,
icon: const Icon(SpotubeIcons.minimize), ),
onPressed: () => Navigator.of(context).pop(), IconButton.ghost(
), icon: const Icon(SpotubeIcons.minimize),
const SizedBox(width: 5), onPressed: () => Navigator.of(context).pop(),
),
const SizedBox(width: 5),
],
),
Expanded(
child: IndexedStack(
index: selectedIndex.value,
children: [
SyncedLyrics(palette: palette, isModal: isModal),
PlainLyrics(palette: palette, isModal: isModal),
], ],
), ),
Expanded( ),
child: IndexedStack( ],
index: selectedIndex.value,
children: [
SyncedLyrics(palette: palette, isModal: isModal),
PlainLyrics(palette: palette, isModal: isModal),
],
),
),
],
),
), ),
), ),
), ),
); );
} }
return SafeArea( return SafeArea(
bottom: mediaQuery.mdAndUp, bottom: false,
child: Scaffold( child: Scaffold(
floatingHeader: true, floatingHeader: true,
headers: [ headers: [
@ -157,13 +161,14 @@ class LyricsPage extends HookConsumerWidget {
image: UniversalImage.imageProvider(albumArt), image: UniversalImage.imageProvider(albumArt),
fit: BoxFit.cover, fit: BoxFit.cover,
), ),
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(10),
),
), ),
margin: const EdgeInsets.only(bottom: 10), margin: const EdgeInsets.only(bottom: 10),
child: BackdropFilter( child: SurfaceCard(
filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5), surfaceBlur: context.theme.surfaceBlur,
surfaceOpacity: context.theme.surfaceOpacity,
padding: EdgeInsets.zero,
borderRadius: BorderRadius.zero,
borderWidth: 0,
child: ColoredBox( child: ColoredBox(
color: palette.color.withOpacity(.7), color: palette.color.withOpacity(.7),
child: SafeArea( child: SafeArea(

View File

@ -1,9 +1,8 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:palette_generator/palette_generator.dart'; import 'package:palette_generator/palette_generator.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/modules/lyrics/zoom_controls.dart'; import 'package:spotube/modules/lyrics/zoom_controls.dart';
import 'package:spotube/components/shimmers/shimmer_lyrics.dart'; import 'package:spotube/components/shimmers/shimmer_lyrics.dart';
@ -30,7 +29,7 @@ class PlainLyrics extends HookConsumerWidget {
final playlist = ref.watch(audioPlayerProvider); final playlist = ref.watch(audioPlayerProvider);
final lyricsQuery = ref.watch(syncedLyricsProvider(playlist.activeTrack)); final lyricsQuery = ref.watch(syncedLyricsProvider(playlist.activeTrack));
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.of(context);
final textTheme = Theme.of(context).textTheme; final typography = Theme.of(context).typography;
final textZoomLevel = useState<int>(defaultTextZoom); final textZoomLevel = useState<int>(defaultTextZoom);
@ -44,9 +43,8 @@ class PlainLyrics extends HookConsumerWidget {
child: Text( child: Text(
playlist.activeTrack?.name ?? "", playlist.activeTrack?.name ?? "",
style: mediaQuery.mdAndUp style: mediaQuery.mdAndUp
? textTheme.displaySmall ? typography.h3
: textTheme.headlineMedium?.copyWith( : typography.h4.copyWith(
fontSize: 25,
color: palette.titleTextColor, color: palette.titleTextColor,
), ),
), ),
@ -54,10 +52,8 @@ class PlainLyrics extends HookConsumerWidget {
Center( Center(
child: Text( child: Text(
playlist.activeTrack?.artists?.asString() ?? "", playlist.activeTrack?.artists?.asString() ?? "",
style: (mediaQuery.mdAndUp style: (mediaQuery.mdAndUp ? typography.h4 : typography.large)
? textTheme.headlineSmall .copyWith(color: palette.bodyTextColor),
: textTheme.titleLarge)
?.copyWith(color: palette.bodyTextColor),
), ),
) )
], ],
@ -79,7 +75,7 @@ class PlainLyrics extends HookConsumerWidget {
children: [ children: [
Text( Text(
context.l10n.no_lyrics_available, context.l10n.no_lyrics_available,
style: textTheme.bodyLarge?.copyWith( style: typography.large.copyWith(
color: palette.bodyTextColor, color: palette.bodyTextColor,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,

View File

@ -1,10 +1,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:palette_generator/palette_generator.dart'; import 'package:palette_generator/palette_generator.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/modules/lyrics/zoom_controls.dart'; import 'package:spotube/modules/lyrics/zoom_controls.dart';
import 'package:spotube/components/shimmers/shimmer_lyrics.dart'; import 'package:spotube/components/shimmers/shimmer_lyrics.dart';
@ -37,7 +36,7 @@ class SyncedLyrics extends HookConsumerWidget {
Widget build(BuildContext context, ref) { Widget build(BuildContext context, ref) {
final playlist = ref.watch(audioPlayerProvider); final playlist = ref.watch(audioPlayerProvider);
final mediaQuery = MediaQuery.of(context); final mediaQuery = MediaQuery.sizeOf(context);
final controller = useAutoScrollController(); final controller = useAutoScrollController();
final delay = ref.watch(syncedLyricsDelayProvider); final delay = ref.watch(syncedLyricsDelayProvider);
@ -54,7 +53,7 @@ class SyncedLyrics extends HookConsumerWidget {
useSyncedLyrics(ref, lyricsState.asData?.value.lyricsMap ?? {}, delay); useSyncedLyrics(ref, lyricsState.asData?.value.lyricsMap ?? {}, delay);
final textZoomLevel = useState<int>(defaultTextZoom); final textZoomLevel = useState<int>(defaultTextZoom);
final textTheme = Theme.of(context).textTheme; final typography = Theme.of(context).typography;
ref.listen( ref.listen(
audioPlayerProvider.select((s) => s.activeTrack), audioPlayerProvider.select((s) => s.activeTrack),
@ -69,11 +68,11 @@ class SyncedLyrics extends HookConsumerWidget {
); );
final headlineTextStyle = (mediaQuery.mdAndUp final headlineTextStyle = (mediaQuery.mdAndUp
? textTheme.displaySmall ? typography.h3
: textTheme.headlineMedium?.copyWith(fontSize: 25)) : typography.h4.copyWith(fontSize: 25))
?.copyWith(color: palette.titleTextColor); .copyWith(color: palette.titleTextColor);
final bodyTextTheme = textTheme.bodyLarge?.copyWith( final bodyTextTheme = typography.large.copyWith(
color: palette.bodyTextColor, color: palette.bodyTextColor,
); );
@ -115,9 +114,8 @@ class SyncedLyrics extends HookConsumerWidget {
preferredSize: const Size.fromHeight(40), preferredSize: const Size.fromHeight(40),
child: Text( child: Text(
playlist.activeTrack?.artists?.asString() ?? "", playlist.activeTrack?.artists?.asString() ?? "",
style: mediaQuery.mdAndUp style:
? textTheme.headlineSmall mediaQuery.mdAndUp ? typography.h4 : typography.x2Large,
: textTheme.titleLarge,
), ),
), ),
), ),
@ -144,7 +142,7 @@ class SyncedLyrics extends HookConsumerWidget {
? Container( ? Container(
padding: index == lyricValue.lyrics.length - 1 padding: index == lyricValue.lyrics.length - 1
? EdgeInsets.only( ? EdgeInsets.only(
bottom: mediaQuery.size.height / 2, bottom: mediaQuery.height / 2,
) )
: null, : null,
) )
@ -165,31 +163,34 @@ class SyncedLyrics extends HookConsumerWidget {
(textZoomLevel.value / 100), (textZoomLevel.value / 100),
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
child: InkWell( child: MouseRegion(
onTap: () async { cursor: SystemMouseCursors.click,
final time = Duration( child: GestureDetector(
seconds: onTap: () async {
lyricSlice.time.inSeconds - delay, final time = Duration(
); seconds:
if (time > audioPlayer.duration || lyricSlice.time.inSeconds - delay,
time.isNegative) { );
return; if (time > audioPlayer.duration ||
} time.isNegative) {
audioPlayer.seek(time); return;
}, }
child: Builder(builder: (context) { audioPlayer.seek(time);
return StrokeText( },
text: lyricSlice.text, child: Builder(builder: (context) {
textStyle: return StrokeText(
DefaultTextStyle.of(context).style, text: lyricSlice.text,
textColor: isActive textStyle:
? Colors.white DefaultTextStyle.of(context).style,
: palette.bodyTextColor, textColor: isActive
strokeColor: isActive ? Colors.white
? Colors.black : palette.bodyTextColor,
: Colors.transparent, strokeColor: isActive
); ? Colors.black
}), : Colors.transparent,
);
}),
),
), ),
), ),
), ),
@ -231,7 +232,7 @@ class SyncedLyrics extends HookConsumerWidget {
), ),
TextSpan( TextSpan(
text: " Plain Lyrics ", text: " Plain Lyrics ",
style: textTheme.bodyLarge?.copyWith( style: typography.large.copyWith(
color: palette.bodyTextColor, color: palette.bodyTextColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),

View File

@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
@ -180,37 +179,16 @@ class RootApp extends HookConsumerWidget {
return getSidebarTileList(context.l10n).map((s) => s.name).toList(); return getSidebarTileList(context.l10n).map((s) => s.name).toList();
}, []); }, []);
final bottomPlayerKey = useMemoized(() => GlobalKey<State>(), []);
final navigationBarKey = useMemoized(() => GlobalKey<State>(), []);
final bottomPadding = useMemoized(() {
return [bottomPlayerKey, navigationBarKey]
.map((k) =>
(k.currentContext?.findRenderObject() as RenderBox?)
?.size
.height ??
0)
.sum;
}, [bottomPlayerKey, navigationBarKey]);
final scaffold = MediaQuery.removeViewInsets( final scaffold = MediaQuery.removeViewInsets(
context: context, context: context,
removeBottom: true, removeBottom: true,
child: Scaffold( child: Scaffold(
footers: [ footers: const [
BottomPlayer(key: bottomPlayerKey), BottomPlayer(),
SpotubeNavigationBar(key: navigationBarKey), SpotubeNavigationBar(),
], ],
floatingFooter: true, floatingFooter: true,
// Fix for safe are not working for bottom bar child: Sidebar(child: child),
child: MediaQuery(
data: MediaQuery.of(context).copyWith(
padding: MediaQuery.of(context).padding.copyWith(
bottom: bottomPadding,
),
),
child: Sidebar(child: child),
),
), ),
); );

View File

@ -1955,8 +1955,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: "5359958464a57235f0f34c43a4623356a32459a0" ref: fix-scaffold-footer-safearea
resolved-ref: "5359958464a57235f0f34c43a4623356a32459a0" resolved-ref: "8ede34e1e3270ec3839192a7a6453e19cd00f9ab"
url: "https://github.com/KRTirtho/shadcn_flutter.git" url: "https://github.com/KRTirtho/shadcn_flutter.git"
source: git source: git
version: "0.0.24" version: "0.0.24"

View File

@ -103,7 +103,7 @@ dependencies:
shadcn_flutter: shadcn_flutter:
git: git:
url: https://github.com/KRTirtho/shadcn_flutter.git url: https://github.com/KRTirtho/shadcn_flutter.git
ref: 5359958464a57235f0f34c43a4623356a32459a0 ref: fix-scaffold-footer-safearea
shared_preferences: ^2.2.3 shared_preferences: ^2.2.3
shelf: ^1.4.1 shelf: ^1.4.1
shelf_router: ^1.1.4 shelf_router: ^1.1.4