redesign app phase 3
Build / Build for Android (push) Has been cancelled
Build / Build for iOS (push) Has been cancelled

This commit is contained in:
Zakaria
2026-05-19 16:41:18 -04:00
parent c36fc55102
commit 9eb6b5237c
20 changed files with 645 additions and 275 deletions
+208 -147
View File
@@ -265,91 +265,87 @@ class _MusicScreenState extends State<MusicScreen>
padding: const EdgeInsets.only(right: 8.0),
child: getFloatingActionButton(),
),
body: DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Theme.of(context)
.colorScheme
.primaryContainer
.withOpacity(0.45),
Theme.of(context).colorScheme.surface,
Theme.of(context).colorScheme.surface,
],
),
),
child: SafeArea(
top: false,
child: Column(
children: [
if (!isSearching)
_MusicHeroHeader(
title: _finampUserHelper
.currentUser?.currentView?.name ??
AppLocalizations.of(context)!.music,
activeTab: tabs
.elementAt(_tabController!.index)
.toLocalisedString(context),
isFavourite: finampSettings.isFavourite,
isOffline: finampSettings.isOffline,
onShuffle: () async {
try {
await _audioServiceHelper.shuffleAll(
FinampSettingsHelper
.finampSettings.isFavourite,
);
} catch (e) {
errorSnackbar(e, context);
}
},
body: Stack(
children: [
const _MusicBackdrop(),
SafeArea(
top: false,
child: Column(
children: [
if (!isSearching)
_MusicHeroHeader(
title: _finampUserHelper
.currentUser?.currentView?.name ??
AppLocalizations.of(context)!.music,
activeTab: tabs
.elementAt(_tabController!.index)
.toLocalisedString(context),
isFavourite: finampSettings.isFavourite,
isOffline: finampSettings.isOffline,
onShuffle: () async {
try {
await _audioServiceHelper.shuffleAll(
FinampSettingsHelper
.finampSettings.isFavourite,
);
} catch (e) {
errorSnackbar(e, context);
}
},
),
_ModernMusicTabBar(
controller: _tabController!,
tabs: tabs.toList(),
),
_ModernMusicTabBar(
controller: _tabController!,
tabs: tabs.toList(),
),
Expanded(
child: Container(
margin: const EdgeInsets.fromLTRB(12, 8, 12, 0),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(28),
),
boxShadow: [
BoxShadow(
color: Theme.of(context)
.colorScheme
.shadow
.withOpacity(0.08),
blurRadius: 24,
offset: const Offset(0, -6),
Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(14, 14, 14, 0),
child: Material(
color: Theme.of(context)
.colorScheme
.surface
.withOpacity(0.74),
shape: RoundedRectangleBorder(
borderRadius: const BorderRadius.vertical(
top: Radius.circular(34),
),
side: BorderSide(
color: Theme.of(context)
.colorScheme
.outlineVariant
.withOpacity(0.42),
),
),
],
),
clipBehavior: Clip.antiAlias,
child: TabBarView(
controller: _tabController,
children: tabs
.map((tabType) => MusicScreenTabView(
tabContentType: tabType,
searchTerm: searchQuery,
isFavourite: finampSettings.isFavourite,
sortBy: finampSettings
.getTabSortBy(tabType),
sortOrder: finampSettings
.getSortOrder(tabType),
view: _finampUserHelper
.currentUser?.currentView,
))
.toList(),
elevation: 16,
shadowColor: Theme.of(context)
.colorScheme
.shadow
.withOpacity(0.24),
clipBehavior: Clip.antiAlias,
child: TabBarView(
controller: _tabController,
children: tabs
.map((tabType) => MusicScreenTabView(
tabContentType: tabType,
searchTerm: searchQuery,
isFavourite:
finampSettings.isFavourite,
sortBy: finampSettings
.getTabSortBy(tabType),
sortOrder: finampSettings
.getSortOrder(tabType),
view: _finampUserHelper
.currentUser?.currentView,
))
.toList(),
),
),
),
),
),
],
],
),
),
),
],
),
),
);
@@ -360,6 +356,78 @@ class _MusicScreenState extends State<MusicScreen>
}
}
class _MusicBackdrop extends StatelessWidget {
const _MusicBackdrop();
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
const Color(0xFF081D38),
colorScheme.background,
const Color(0xFF020611),
],
),
),
child: Stack(
children: [
Positioned(
top: -110,
right: -80,
child: _GlowOrb(
color: colorScheme.primary.withOpacity(0.28),
size: 280,
),
),
Positioned(
top: 90,
left: -120,
child: _GlowOrb(
color: colorScheme.tertiary.withOpacity(0.20),
size: 260,
),
),
Positioned(
bottom: -140,
right: -130,
child: _GlowOrb(
color: colorScheme.secondary.withOpacity(0.16),
size: 320,
),
),
],
),
);
}
}
class _GlowOrb extends StatelessWidget {
const _GlowOrb({required this.color, required this.size});
final Color color;
final double size;
@override
Widget build(BuildContext context) {
return Container(
width: size,
height: size,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: RadialGradient(
colors: [color, Colors.transparent],
),
),
);
}
}
class _MusicHeroHeader extends StatelessWidget {
const _MusicHeroHeader({
required this.title,
@@ -381,73 +449,64 @@ class _MusicHeroHeader extends StatelessWidget {
final colorScheme = theme.colorScheme;
return Padding(
padding: const EdgeInsets.fromLTRB(20, 86, 20, 12),
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(32),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
colorScheme.primary.withOpacity(0.95),
colorScheme.tertiary.withOpacity(0.78),
],
),
boxShadow: [
BoxShadow(
color: colorScheme.primary.withOpacity(0.28),
blurRadius: 34,
offset: const Offset(0, 18),
padding: const EdgeInsets.fromLTRB(24, 88, 24, 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
isOffline ? 'OFFLINE MODE' : 'FINAMP MUSIC',
style: theme.textTheme.labelMedium?.copyWith(
color: colorScheme.primary,
fontWeight: FontWeight.w800,
letterSpacing: 2.2,
),
],
),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
),
const SizedBox(height: 10),
Text(
title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.displaySmall?.copyWith(
color: colorScheme.onBackground,
fontWeight: FontWeight.w900,
height: 0.98,
letterSpacing: -1.4,
),
),
const SizedBox(height: 12),
Text(
'Browse your Jellyfin collection with artwork-first shelves.',
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodyMedium?.copyWith(
color: colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 22),
Row(
children: [
Text(
isOffline ? 'Offline library' : 'Your Jellyfin music',
style: theme.textTheme.labelLarge?.copyWith(
color: colorScheme.onPrimary.withOpacity(0.76),
letterSpacing: 0.6,
FilledButton.icon(
onPressed: onShuffle,
icon: const Icon(Icons.shuffle_rounded),
label: const Text('Shuffle'),
style: FilledButton.styleFrom(
backgroundColor: colorScheme.primary,
foregroundColor: colorScheme.onPrimary,
padding: const EdgeInsets.symmetric(
horizontal: 18,
vertical: 14,
),
),
),
const SizedBox(height: 8),
Text(
title,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.headlineMedium?.copyWith(
color: colorScheme.onPrimary,
fontWeight: FontWeight.w800,
height: 1.04,
),
),
const SizedBox(height: 20),
Row(
children: [
FilledButton.icon(
onPressed: onShuffle,
icon: const Icon(Icons.shuffle_rounded),
label: const Text('Shuffle'),
style: FilledButton.styleFrom(
backgroundColor: colorScheme.onPrimary,
foregroundColor: colorScheme.primary,
),
),
const SizedBox(width: 10),
_HeroChip(
icon: isFavourite
? Icons.favorite_rounded
: Icons.library_music_rounded,
label: isFavourite ? 'Favourites' : activeTab,
),
],
const SizedBox(width: 12),
_HeroChip(
icon:
isFavourite ? Icons.favorite_rounded : Icons.album_rounded,
label: isFavourite ? 'Favourites' : activeTab,
),
],
),
),
],
),
);
}
@@ -466,19 +525,19 @@ class _HeroChip extends StatelessWidget {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: colorScheme.onPrimary.withOpacity(0.14),
color: colorScheme.surfaceVariant.withOpacity(0.58),
borderRadius: BorderRadius.circular(999),
border: Border.all(color: colorScheme.onPrimary.withOpacity(0.16)),
border: Border.all(color: colorScheme.outlineVariant.withOpacity(0.55)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(icon, color: colorScheme.onPrimary, size: 18),
Icon(icon, color: colorScheme.secondary, size: 18),
const SizedBox(width: 8),
Text(
label,
style: TextStyle(
color: colorScheme.onPrimary,
color: colorScheme.onSurface,
fontWeight: FontWeight.w700,
),
),
@@ -502,10 +561,10 @@ class _ModernMusicTabBar extends StatelessWidget {
padding: const EdgeInsets.symmetric(horizontal: 12),
child: DecoratedBox(
decoration: BoxDecoration(
color: colorScheme.surface.withOpacity(0.7),
color: colorScheme.surfaceVariant.withOpacity(0.50),
borderRadius: BorderRadius.circular(999),
border:
Border.all(color: colorScheme.outlineVariant.withOpacity(0.4)),
Border.all(color: colorScheme.outlineVariant.withOpacity(0.55)),
),
child: TabBar(
controller: controller,
@@ -518,7 +577,9 @@ class _ModernMusicTabBar extends StatelessWidget {
indicatorSize: TabBarIndicatorSize.tab,
indicator: BoxDecoration(
borderRadius: BorderRadius.circular(999),
color: colorScheme.primary,
gradient: LinearGradient(
colors: [colorScheme.primary, colorScheme.secondary],
),
),
labelColor: colorScheme.onPrimary,
unselectedLabelColor: colorScheme.onSurfaceVariant,