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),
|
scale: Tween<double>(begin: 1, end: 1.03).animate(animation),
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
|
// ignore: deprecated_member_use
|
||||||
onReorder: (oldIndex, newIndex) async {
|
onReorder: (oldIndex, newIndex) async {
|
||||||
if (newIndex > oldIndex) newIndex--;
|
if (newIndex > oldIndex) newIndex--;
|
||||||
final emisora = favoritos[oldIndex];
|
final emisora = favoritos[oldIndex];
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ class _PantallaInicioState extends State<PantallaInicio> {
|
|||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverToBoxAdapter(child: _heroHeader(context, estado)),
|
SliverToBoxAdapter(child: _heroHeader(context, estado)),
|
||||||
|
const SliverToBoxAdapter(child: _AuroraWaveBanner()),
|
||||||
SliverToBoxAdapter(child: _seccionTendencias(estado, theme)),
|
SliverToBoxAdapter(child: _seccionTendencias(estado, theme)),
|
||||||
SliverToBoxAdapter(child: _chipGeneros(context, theme)),
|
SliverToBoxAdapter(child: _chipGeneros(context, theme)),
|
||||||
if (estado.error != null)
|
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 {
|
class _ChipShimmer extends StatelessWidget {
|
||||||
final ThemeData theme;
|
final ThemeData theme;
|
||||||
const _ChipShimmer({required this.theme});
|
const _ChipShimmer({required this.theme});
|
||||||
|
|||||||
@@ -24,21 +24,34 @@ class PluriIcon extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final tokens = context.pluriTokens;
|
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(),
|
_resolveData(),
|
||||||
size: size,
|
size: size,
|
||||||
color: _resolveColor(context, tokens),
|
color: resolvedColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
final child = variant == PluriIconVariant.activeGlow
|
final child = variant == PluriIconVariant.activeGlow
|
||||||
? Container(
|
? Container(
|
||||||
width: size + 12,
|
width: size + 14,
|
||||||
height: size + 12,
|
height: size + 14,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
boxShadow: [
|
boxShadow: [
|
||||||
BoxShadow(
|
BoxShadow(
|
||||||
color: tokens.glowColor,
|
color: tokens.glowColor,
|
||||||
blurRadius: 14,
|
blurRadius: 18,
|
||||||
spreadRadius: 1,
|
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) {
|
Color _resolveColor(BuildContext context, PluriWaveTokens tokens) {
|
||||||
if (variant == PluriIconVariant.activeGlow) return tokens.electricMagenta;
|
if (variant == PluriIconVariant.activeGlow) return tokens.electricMagenta;
|
||||||
if (variant == PluriIconVariant.filled) return Theme.of(context).colorScheme.onSurface;
|
if (variant == PluriIconVariant.filled) return Theme.of(context).colorScheme.onSurface;
|
||||||
|
|||||||
@@ -38,6 +38,19 @@ class PluriScreenHeader extends StatelessWidget {
|
|||||||
top: -42,
|
top: -42,
|
||||||
child: _Orb(color: t.electricMagenta.withValues(alpha: 0.38), size: 128),
|
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(
|
Positioned(
|
||||||
right: 44,
|
right: 44,
|
||||||
bottom: -54,
|
bottom: -54,
|
||||||
|
|||||||
@@ -293,25 +293,48 @@ class _TarjetaEmisoraState extends State<TarjetaEmisora> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _iconoFallback(double size) {
|
Widget _iconoFallback(double size) {
|
||||||
final t = context.pluriTokens;
|
final art = _fallbackArtFor(widget.emisora.uuid);
|
||||||
return DecoratedBox(
|
return Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
art,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorBuilder: (_, __, ___) => DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
begin: Alignment.topLeft,
|
begin: Alignment.topLeft,
|
||||||
end: Alignment.bottomRight,
|
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(
|
child: PluriIcon(
|
||||||
glyph: PluriIconGlyph.player,
|
glyph: PluriIconGlyph.player,
|
||||||
variant: PluriIconVariant.filled,
|
variant: PluriIconVariant.activeGlow,
|
||||||
size: size,
|
size: size,
|
||||||
semanticLabel: 'Icono de emisora',
|
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 {
|
class _LiveBadge extends StatelessWidget {
|
||||||
|
|||||||
@@ -63,3 +63,4 @@ flutter:
|
|||||||
- assets/images/
|
- assets/images/
|
||||||
- assets/icons/
|
- assets/icons/
|
||||||
- assets/mockups/
|
- assets/mockups/
|
||||||
|
- assets/generated/
|
||||||
|
|||||||