feat(ui): add generated premium assets
Build & Deploy Pluriwave / Build APK + AAB release (push) Successful in 1m17s
Build & Deploy Pluriwave / Análisis de código (push) Successful in 11s

This commit is contained in:
2026-05-20 22:15:16 +02:00
parent 2fb794a43b
commit 3be59d740c
18 changed files with 146 additions and 23 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 436 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

+1
View File
@@ -56,6 +56,7 @@ class PantallaFavoritos extends StatelessWidget {
scale: Tween<double>(begin: 1, end: 1.03).animate(animation),
child: child,
),
// ignore: deprecated_member_use
onReorder: (oldIndex, newIndex) async {
if (newIndex > oldIndex) newIndex--;
final emisora = favoritos[oldIndex];
+60
View File
@@ -46,6 +46,7 @@ class _PantallaInicioState extends State<PantallaInicio> {
child: CustomScrollView(
slivers: [
SliverToBoxAdapter(child: _heroHeader(context, estado)),
const SliverToBoxAdapter(child: _AuroraWaveBanner()),
SliverToBoxAdapter(child: _seccionTendencias(estado, theme)),
SliverToBoxAdapter(child: _chipGeneros(context, theme)),
if (estado.error != null)
@@ -238,6 +239,65 @@ class _PantallaInicioState extends State<PantallaInicio> {
}
}
class _AuroraWaveBanner extends StatelessWidget {
const _AuroraWaveBanner();
@override
Widget build(BuildContext context) {
final t = context.pluriTokens;
return Padding(
padding: EdgeInsets.fromLTRB(t.spacingMd, 4, t.spacingMd, 8),
child: ClipRRect(
borderRadius: BorderRadius.circular(t.radiusLg),
child: Stack(
alignment: Alignment.centerLeft,
children: [
Image.asset(
'assets/images/aurora_wave_banner.png',
height: 108,
width: double.infinity,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
),
Container(
height: 108,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black.withValues(alpha: 0.58),
Colors.transparent,
],
),
),
),
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Live spectrum',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w900,
),
),
const SizedBox(height: 4),
Text(
'Ondas aurora en tiempo real',
style: Theme.of(context).textTheme.bodySmall,
),
],
),
),
],
),
),
);
}
}
class _ChipShimmer extends StatelessWidget {
final ThemeData theme;
const _ChipShimmer({required this.theme});
+33 -8
View File
@@ -24,21 +24,34 @@ class PluriIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
final tokens = context.pluriTokens;
final icon = Icon(
_resolveData(),
size: size,
color: _resolveColor(context, tokens),
);
final asset = _resolveAsset();
final resolvedColor = _resolveColor(context, tokens);
final icon = asset == null
? Icon(_resolveData(), size: size, color: resolvedColor)
: Opacity(
opacity: variant == PluriIconVariant.outline ? 0.78 : 1,
child: Image.asset(
asset,
width: size,
height: size,
fit: BoxFit.contain,
errorBuilder: (_, __, ___) => Icon(
_resolveData(),
size: size,
color: resolvedColor,
),
),
);
final child = variant == PluriIconVariant.activeGlow
? Container(
width: size + 12,
height: size + 12,
width: size + 14,
height: size + 14,
decoration: BoxDecoration(
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: tokens.glowColor,
blurRadius: 14,
blurRadius: 18,
spreadRadius: 1,
),
],
@@ -55,6 +68,18 @@ class PluriIcon extends StatelessWidget {
);
}
String? _resolveAsset() {
return switch (glyph) {
PluriIconGlyph.home => 'assets/icons/pluri_home.png',
PluriIconGlyph.search => 'assets/icons/pluri_search.png',
PluriIconGlyph.favorites => 'assets/icons/pluri_favorites.png',
PluriIconGlyph.player => 'assets/icons/pluri_player.png',
PluriIconGlyph.settings => 'assets/icons/pluri_settings.png',
};
}
Color _resolveColor(BuildContext context, PluriWaveTokens tokens) {
if (variant == PluriIconVariant.activeGlow) return tokens.electricMagenta;
if (variant == PluriIconVariant.filled) return Theme.of(context).colorScheme.onSurface;
+13
View File
@@ -38,6 +38,19 @@ class PluriScreenHeader extends StatelessWidget {
top: -42,
child: _Orb(color: t.electricMagenta.withValues(alpha: 0.38), size: 128),
),
Positioned(
right: 10,
top: 10,
child: Opacity(
opacity: 0.18,
child: Image.asset(
'assets/icons/pluriwave_app_mark.png',
width: 120,
height: 120,
errorBuilder: (_, __, ___) => const SizedBox.shrink(),
),
),
),
Positioned(
right: 44,
bottom: -54,
+38 -15
View File
@@ -293,25 +293,48 @@ class _TarjetaEmisoraState extends State<TarjetaEmisora> {
}
Widget _iconoFallback(double size) {
final t = context.pluriTokens;
return DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [t.deepViolet, t.electricMagenta.withValues(alpha: 0.8)],
final art = _fallbackArtFor(widget.emisora.uuid);
return Stack(
fit: StackFit.expand,
children: [
Image.asset(
art,
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
context.pluriTokens.deepViolet,
context.pluriTokens.electricMagenta.withValues(alpha: 0.8),
],
),
),
),
),
),
child: Center(
child: PluriIcon(
glyph: PluriIconGlyph.player,
variant: PluriIconVariant.filled,
size: size,
semanticLabel: 'Icono de emisora',
Center(
child: PluriIcon(
glyph: PluriIconGlyph.player,
variant: PluriIconVariant.activeGlow,
size: size,
semanticLabel: 'Icono de emisora',
),
),
),
],
);
}
String _fallbackArtFor(String seed) {
const arts = [
'assets/images/station_art_aurora.png',
'assets/images/station_art_cosmic.png',
'assets/images/station_art_pulse.png',
'assets/images/station_art_nova.png',
];
final index = seed.codeUnits.fold<int>(0, (a, b) => a + b) % arts.length;
return arts[index];
}
}
class _LiveBadge extends StatelessWidget {
+1
View File
@@ -63,3 +63,4 @@ flutter:
- assets/images/
- assets/icons/
- assets/mockups/
- assets/generated/