feat(quality): harden lint rules and add quality-gate tests

This commit is contained in:
2026-06-12 00:05:06 +02:00
parent 202bef3539
commit 8a032e6e62
21 changed files with 485 additions and 140 deletions
+54 -23
View File
@@ -1,4 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import '../l10n/gen/app_localizations.dart';
import '../modelos/preset_ecualizador.dart';
@@ -9,7 +9,11 @@ class EcualizadorWidget extends StatefulWidget {
final PresetEcualizador preset;
final void Function(PresetEcualizador) onCambio;
const EcualizadorWidget({super.key, required this.preset, required this.onCambio});
const EcualizadorWidget({
super.key,
required this.preset,
required this.onCambio,
});
@override
State<EcualizadorWidget> createState() => _EcualizadorWidgetState();
@@ -35,7 +39,9 @@ class _EcualizadorWidgetState extends State<EcualizadorWidget> {
void _actualizarBanda(int index, double valor) {
setState(() => _bandas[index] = valor);
widget.onCambio(PresetEcualizador(nombre: 'Personalizado', bandas: List.from(_bandas)));
widget.onCambio(
PresetEcualizador(nombre: 'Personalizado', bandas: List.from(_bandas)),
);
}
@override
@@ -52,11 +58,20 @@ class _EcualizadorWidgetState extends State<EcualizadorWidget> {
children: [
Row(
children: [
Text(l10n.equalizerTitle, style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w700)),
Text(
l10n.equalizerTitle,
style: theme.textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w700,
),
),
const Spacer(),
Chip(
label: Text(_nombrePreset(l10n, widget.preset.nombre), style: theme.textTheme.labelMedium),
backgroundColor: theme.colorScheme.secondaryContainer.withValues(alpha: 0.75),
label: Text(
_nombrePreset(l10n, widget.preset.nombre),
style: theme.textTheme.labelMedium,
),
backgroundColor: theme.colorScheme.secondaryContainer
.withValues(alpha: 0.75),
),
],
),
@@ -68,11 +83,18 @@ class _EcualizadorWidgetState extends State<EcualizadorWidget> {
for (int i = 0; i < 5; i++)
Expanded(
child: Card(
color: theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.35),
color: theme.colorScheme.surfaceContainerHighest.withValues(
alpha: 0.35,
),
margin: const EdgeInsets.symmetric(horizontal: 4),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 4),
padding: const EdgeInsets.symmetric(
vertical: 8,
horizontal: 4,
),
child: Column(
children: [
SizedBox(
@@ -80,7 +102,9 @@ class _EcualizadorWidgetState extends State<EcualizadorWidget> {
child: Semantics(
slider: true,
label: l10n.equalizerBandLabel(_etiquetas[i]),
value: l10n.equalizerBandValue(_bandas[i].toStringAsFixed(1)),
value: l10n.equalizerBandValue(
_bandas[i].toStringAsFixed(1),
),
child: RotatedBox(
quarterTurns: 3,
child: Slider(
@@ -93,10 +117,15 @@ class _EcualizadorWidgetState extends State<EcualizadorWidget> {
),
),
),
Text('${_bandas[i].toStringAsFixed(1)}dB', style: theme.textTheme.labelSmall),
Text(
'${_bandas[i].toStringAsFixed(1)}dB',
style: theme.textTheme.labelSmall,
),
Text(
_etiquetas[i],
style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.onSurfaceVariant),
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
textAlign: TextAlign.center,
),
],
@@ -142,17 +171,19 @@ class PresetsEcualizadorWidget extends StatelessWidget {
return Wrap(
spacing: 8,
runSpacing: 6,
children: PresetEcualizador.presets.map((p) {
final selected = p.nombre == presetActual.nombre;
return ChoiceChip(
label: Text(_nombrePreset(l10n, p.nombre)),
selected: selected,
showCheckmark: false,
selectedColor: theme.colorScheme.primaryContainer,
backgroundColor: theme.colorScheme.surfaceContainerHighest.withValues(alpha: 0.32),
onSelected: (_) => onSeleccionar(p),
);
}).toList(),
children:
PresetEcualizador.presets.map((p) {
final selected = p.nombre == presetActual.nombre;
return ChoiceChip(
label: Text(_nombrePreset(l10n, p.nombre)),
selected: selected,
showCheckmark: false,
selectedColor: theme.colorScheme.primaryContainer,
backgroundColor: theme.colorScheme.surfaceContainerHighest
.withValues(alpha: 0.32),
onSelected: (_) => onSeleccionar(p),
);
}).toList(),
);
}
}
+47 -40
View File
@@ -1,4 +1,4 @@
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import '../tema/pluriwave_theme.dart';
import 'pluri_glass_surface.dart';
@@ -72,29 +72,32 @@ class _PluriNavButton extends StatelessWidget {
margin: EdgeInsets.symmetric(horizontal: selected ? 3 : 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(999),
gradient: selected
? LinearGradient(
colors: [
t.electricMagenta.withValues(alpha: 0.32),
t.warmCoral.withValues(alpha: 0.18),
],
)
: null,
gradient:
selected
? LinearGradient(
colors: [
t.electricMagenta.withValues(alpha: 0.32),
t.warmCoral.withValues(alpha: 0.18),
],
)
: null,
border: Border.all(
color: selected
? Colors.white.withValues(alpha: 0.22)
: Colors.white.withValues(alpha: 0.06),
color:
selected
? Colors.white.withValues(alpha: 0.22)
: Colors.white.withValues(alpha: 0.06),
),
boxShadow: selected
? [
BoxShadow(
color: t.glowColor.withValues(alpha: 0.36),
blurRadius: 24,
spreadRadius: -6,
offset: const Offset(0, 8),
),
]
: const [],
boxShadow:
selected
? [
BoxShadow(
color: t.glowColor.withValues(alpha: 0.36),
blurRadius: 24,
spreadRadius: -6,
offset: const Offset(0, 8),
),
]
: const [],
),
child: InkWell(
borderRadius: BorderRadius.circular(999),
@@ -113,30 +116,34 @@ class _PluriNavButton extends StatelessWidget {
curve: Curves.easeOutBack,
child: PluriIcon(
glyph: item.glyph,
variant: selected
? PluriIconVariant.activeGlow
: PluriIconVariant.filled,
variant:
selected
? PluriIconVariant.activeGlow
: PluriIconVariant.filled,
size: selected ? 42 : 34,
),
),
AnimatedSize(
duration: context.pluriMotion.quick,
curve: Curves.easeOutCubic,
child: selected
? Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
item.label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context).textTheme.labelSmall?.copyWith(
color: foreground,
fontWeight: FontWeight.w900,
letterSpacing: -0.2,
),
),
)
: const SizedBox.shrink(),
child:
selected
? Padding(
padding: const EdgeInsets.only(top: 2),
child: Text(
item.label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(
context,
).textTheme.labelSmall?.copyWith(
color: foreground,
fontWeight: FontWeight.w900,
letterSpacing: -0.2,
),
),
)
: const SizedBox.shrink(),
),
],
),
+2 -1
View File
@@ -63,7 +63,8 @@ class PluriIcon extends StatelessWidget {
: icon;
return Semantics(
label: semanticLabel ?? _fallbackLabel(AppLocalizations.of(context), glyph),
label:
semanticLabel ?? _fallbackLabel(AppLocalizations.of(context), glyph),
image: true,
child: ExcludeSemantics(child: child),
);