fix(flutter): full player unmounts the shell — no doubled player UI
You correctly diagnosed: the mini bar was still visible underneath the full player, and that's also why the bottom controls overflowed by 45px (the shell's mini bar was eating the bottom 88dp). Move /now-playing OUT of the ShellRoute and up to a top-level GoRoute. Now pushing /now-playing unmounts the shell entirely — no mini bar underneath, no fight for the bottom strip. The slide-up transition still feels right because the shell stays painted for the duration of the animation (so during the transition both are briefly visible, as you noted is fine). Drop the hideMini conditional in _ShellWithPlayerBar — no longer needed since the shell isn't even built when the full player is on top. Recovering the 88dp also resolves the bottom-controls overflow without needing to shrink the layout.
This commit is contained in:
@@ -55,6 +55,34 @@ GoRouter buildRouter(Ref ref) {
|
||||
GoRoute(path: '/', redirect: (_, __) => '/home'),
|
||||
GoRoute(path: '/server-url', builder: (_, __) => const ServerUrlScreen()),
|
||||
GoRoute(path: '/login', builder: (_, __) => const LoginScreen()),
|
||||
// /now-playing lives outside the ShellRoute on purpose. The full
|
||||
// player IS the player UI when active, and we don't want the mini
|
||||
// bar from the shell underneath fighting for the bottom strip.
|
||||
// Pushing this route unmounts the shell entirely; the slide-up
|
||||
// transition still feels right because the shell stays painted
|
||||
// for the duration of the animation.
|
||||
GoRoute(
|
||||
path: '/now-playing',
|
||||
pageBuilder: (_, __) => CustomTransitionPage(
|
||||
child: const NowPlayingScreen(),
|
||||
transitionDuration: const Duration(milliseconds: 280),
|
||||
reverseTransitionDuration: const Duration(milliseconds: 240),
|
||||
transitionsBuilder: (_, anim, __, child) {
|
||||
final eased = CurvedAnimation(
|
||||
parent: anim,
|
||||
curve: Curves.easeOutCubic,
|
||||
reverseCurve: Curves.easeInCubic,
|
||||
);
|
||||
return SlideTransition(
|
||||
position: Tween(
|
||||
begin: const Offset(0, 1),
|
||||
end: Offset.zero,
|
||||
).animate(eased),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
ShellRoute(
|
||||
builder: (ctx, state, child) => VersionGate(child: _ShellWithPlayerBar(child: child)),
|
||||
routes: [
|
||||
@@ -67,32 +95,6 @@ GoRouter buildRouter(Ref ref) {
|
||||
path: '/albums/:id',
|
||||
builder: (_, s) => AlbumDetailScreen(id: s.pathParameters['id']!),
|
||||
),
|
||||
GoRoute(
|
||||
path: '/now-playing',
|
||||
// Slide-up transition: full player ascends from the bottom
|
||||
// edge (matching where the mini player sits) into a full
|
||||
// screen view. Drag-down dismissal in NowPlayingScreen
|
||||
// mirrors this with a slide back down via Navigator.pop.
|
||||
pageBuilder: (_, __) => CustomTransitionPage(
|
||||
child: const NowPlayingScreen(),
|
||||
transitionDuration: const Duration(milliseconds: 280),
|
||||
reverseTransitionDuration: const Duration(milliseconds: 240),
|
||||
transitionsBuilder: (_, anim, __, child) {
|
||||
final eased = CurvedAnimation(
|
||||
parent: anim,
|
||||
curve: Curves.easeOutCubic,
|
||||
reverseCurve: Curves.easeInCubic,
|
||||
);
|
||||
return SlideTransition(
|
||||
position: Tween(
|
||||
begin: const Offset(0, 1),
|
||||
end: Offset.zero,
|
||||
).animate(eased),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
GoRoute(path: '/queue', builder: (_, __) => const QueueScreen()),
|
||||
GoRoute(path: '/search', builder: (_, __) => const SearchScreen()),
|
||||
GoRoute(path: '/library', builder: (_, __) => const LibraryScreen()),
|
||||
@@ -126,11 +128,6 @@ class _ShellWithPlayerBar extends ConsumerWidget {
|
||||
// When the banner is showing, strip that inset from the child so
|
||||
// its AppBar lands directly under the banner.
|
||||
final hasBanner = ref.watch(shouldShowUpdateBannerProvider) != null;
|
||||
// Hide the mini player when the full player is on top — the full
|
||||
// player IS the player UI in that state, and overlapping them
|
||||
// would just fight for visual space.
|
||||
final loc = GoRouterState.of(context).matchedLocation;
|
||||
final hideMini = loc == '/now-playing';
|
||||
return Column(
|
||||
children: [
|
||||
const UpdateBanner(),
|
||||
@@ -143,7 +140,7 @@ class _ShellWithPlayerBar extends ConsumerWidget {
|
||||
)
|
||||
: child,
|
||||
),
|
||||
if (!hideMini) const PlayerBar(),
|
||||
const PlayerBar(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user