feat(ui): add generated premium assets
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 82 KiB |
|
After Width: | Height: | Size: 565 KiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 436 KiB |
|
After Width: | Height: | Size: 487 KiB |
|
After Width: | Height: | Size: 484 KiB |
|
After Width: | Height: | Size: 353 KiB |
@@ -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];
|
||||
|
||||
@@ -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});
|
||||
|
||||
@@ -24,21 +24,34 @@ class PluriIcon extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final tokens = context.pluriTokens;
|
||||
final icon = Icon(
|
||||
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: _resolveColor(context, tokens),
|
||||
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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -293,25 +293,48 @@ class _TarjetaEmisoraState extends State<TarjetaEmisora> {
|
||||
}
|
||||
|
||||
Widget _iconoFallback(double size) {
|
||||
final t = context.pluriTokens;
|
||||
return DecoratedBox(
|
||||
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: [t.deepViolet, t.electricMagenta.withValues(alpha: 0.8)],
|
||||
colors: [
|
||||
context.pluriTokens.deepViolet,
|
||||
context.pluriTokens.electricMagenta.withValues(alpha: 0.8),
|
||||
],
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: PluriIcon(
|
||||
glyph: PluriIconGlyph.player,
|
||||
variant: PluriIconVariant.filled,
|
||||
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 {
|
||||
|
||||
@@ -63,3 +63,4 @@ flutter:
|
||||
- assets/images/
|
||||
- assets/icons/
|
||||
- assets/mockups/
|
||||
- assets/generated/
|
||||
|
||||