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

View File

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

View File

@ -53,6 +53,7 @@ class UserArtists extends HookConsumerWidget {
}
return SafeArea(
bottom: false,
child: Scaffold(
child: RefreshTrigger(
// 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);
// },
child: SafeArea(
bottom: false,
child: InterScrollbar(
controller: controller,
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:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/spotube_icons.dart';
@ -32,7 +33,7 @@ class ZoomControls extends HookWidget {
@override
Widget build(BuildContext context) {
final actions = [
IconButton(
IconButton.ghost(
icon: decreaseIcon,
onPressed: () {
if (value == min) return;
@ -40,7 +41,7 @@ class ZoomControls extends HookWidget {
},
),
Text("$value$unit"),
IconButton(
IconButton.ghost(
icon: increaseIcon,
onPressed: () {
if (value == max) return;
@ -50,27 +51,28 @@ class ZoomControls extends HookWidget {
];
return Container(
decoration: BoxDecoration(
color: Theme.of(context).cardColor.withOpacity(0.7),
borderRadius: BorderRadius.circular(10),
),
constraints: BoxConstraints(
maxHeight: direction == Axis.horizontal ? 50 : 200,
maxWidth: direction == Axis.vertical ? 50 : double.infinity,
),
margin: const EdgeInsets.all(8),
child: direction == Axis.horizontal
? Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: actions,
)
: Column(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
verticalDirection: VerticalDirection.up,
children: actions,
),
child: SurfaceCard(
surfaceBlur: context.theme.surfaceBlur,
surfaceOpacity: context.theme.surfaceOpacity,
padding: EdgeInsets.zero,
child: direction == Axis.horizontal
? Row(
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
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),
),
),
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context)
.size
.height *
0.8,
),
builder: (context) =>
const LyricsPage(isModal: true),
);

View File

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

View File

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

View File

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

View File

@ -1,6 +1,5 @@
import 'dart:async';
import 'package:collection/collection.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.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();
}, []);
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(
context: context,
removeBottom: true,
child: Scaffold(
footers: [
BottomPlayer(key: bottomPlayerKey),
SpotubeNavigationBar(key: navigationBarKey),
footers: const [
BottomPlayer(),
SpotubeNavigationBar(),
],
floatingFooter: true,
// Fix for safe are not working for bottom bar
child: MediaQuery(
data: MediaQuery.of(context).copyWith(
padding: MediaQuery.of(context).padding.copyWith(
bottom: bottomPadding,
),
),
child: Sidebar(child: child),
),
child: Sidebar(child: child),
),
);

View File

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

View File

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