fix(favoritos): header actions row, chip colours
Audit 4.1 (t4:216): the manage-groups (create_new_folder) and sort (swap_vert) actions now live in PluriRootHeader's own actions slot as two icon buttons -- were an ActionChip inside the chip strip plus a PopupMenuButton sharing a Row with it. The prototype's own back arrow stays absent (binding decision: this root keeps its bottom tab bar, unlike the prototype's pushed-with-back-arrow shape) -- this closes the remaining gap in what was a partial fix. Audit 4.2 (t4:219-221): group filter chips are solid brand teal with dark text when active, listSurface + a faint border when not -- was Material's own ChoiceChip theming (electricMagenta@24% selected). Updated pantalla_favoritos_test.dart's two "Manage lists" text finders to locate the relocated action by key instead (the action is now an icon-only IconButton with a tooltip, not a labelled chip).
This commit is contained in:
@@ -6,6 +6,7 @@ import '../l10n/display_names.dart';
|
||||
import '../l10n/gen/app_localizations.dart';
|
||||
import '../modelos/emisora.dart';
|
||||
import '../modelos/grupo_favoritos.dart';
|
||||
import '../tema/pluriwave_tokens.dart';
|
||||
import '../widgets/fila_emisora_plana.dart';
|
||||
import '../widgets/pluri_icon.dart';
|
||||
import '../widgets/pluri_layout.dart';
|
||||
@@ -186,22 +187,23 @@ class _PantallaFavoritosState extends State<PantallaFavoritos> {
|
||||
children: [
|
||||
// S1/S2 (Tier 1 visual fidelity): see the empty-state branch
|
||||
// above — PluriScreenHeader is retired everywhere.
|
||||
//
|
||||
// Audit 4.1 (t4:216): the prototype's two header icon actions
|
||||
// (create_new_folder, swap_vert) now live in PluriRootHeader's
|
||||
// own actions slot -- they used to be scattered as an
|
||||
// ActionChip inside the chip strip and a PopupMenuButton
|
||||
// sharing a Row with it. The back arrow the prototype also
|
||||
// draws stays absent (binding decision: this root keeps its
|
||||
// bottom tab bar, unlike the prototype's own pushed shape).
|
||||
PluriRootHeader(
|
||||
title: l10n.favoritesTitle,
|
||||
onSleepTimer: () => showPluriSleepTimerSheet(context),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: _FilaChipsGrupos(
|
||||
grupos: gruposVisibles,
|
||||
favoritos: favoritos,
|
||||
seleccionado: seleccionEfectiva,
|
||||
onSeleccionar:
|
||||
(id) => setState(() => _grupoSeleccionadoId = id),
|
||||
onGestionar: _abrirGestionDeListas,
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
key: const ValueKey('favorites-manage-groups-action'),
|
||||
icon: const Icon(Icons.create_new_folder_rounded),
|
||||
tooltip: l10n.favoriteGroupsManage,
|
||||
onPressed: _abrirGestionDeListas,
|
||||
),
|
||||
PopupMenuButton<OrdenEmisoras>(
|
||||
icon: const Icon(Icons.swap_vert_rounded),
|
||||
@@ -221,6 +223,13 @@ class _PantallaFavoritosState extends State<PantallaFavoritos> {
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_FilaChipsGrupos(
|
||||
grupos: gruposVisibles,
|
||||
favoritos: favoritos,
|
||||
seleccionado: seleccionEfectiva,
|
||||
onSeleccionar: (id) => setState(() => _grupoSeleccionadoId = id),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -256,18 +265,45 @@ class _FilaChipsGrupos extends StatelessWidget {
|
||||
required this.favoritos,
|
||||
required this.seleccionado,
|
||||
required this.onSeleccionar,
|
||||
required this.onGestionar,
|
||||
});
|
||||
|
||||
final List<GrupoFavoritos> grupos;
|
||||
final List<Emisora> favoritos;
|
||||
final String? seleccionado;
|
||||
final ValueChanged<String?> onSeleccionar;
|
||||
final VoidCallback onGestionar;
|
||||
|
||||
String _nombreVisible(AppLocalizations l10n, GrupoFavoritos grupo) =>
|
||||
grupo.esSinAsignar ? l10n.favoriteGroupsUnassigned : grupo.nombre;
|
||||
|
||||
/// Audit 4.2 (t4:219-221): active `#21D4D9`/`#062126` w800, inactive
|
||||
/// `listSurface` + a faint border / w700 -- was Material's own
|
||||
/// `ChoiceChip` theming (a plain checkbox-style selected fill).
|
||||
Widget _chip({
|
||||
required String label,
|
||||
required bool selected,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return ChoiceChip(
|
||||
label: Text(label),
|
||||
labelStyle: TextStyle(
|
||||
fontWeight: selected ? FontWeight.w800 : FontWeight.w700,
|
||||
color: selected ? const Color(0xFF062126) : const Color(0xFFF2F7FA),
|
||||
),
|
||||
selected: selected,
|
||||
showCheckmark: false,
|
||||
selectedColor: PluriWaveTokens.brand,
|
||||
backgroundColor: PluriWaveTokens.dark.listSurface,
|
||||
side: BorderSide(
|
||||
color:
|
||||
selected
|
||||
? Colors.transparent
|
||||
: Colors.white.withValues(alpha: 0.09),
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
onSelected: (_) => onTap(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
@@ -278,38 +314,27 @@ class _FilaChipsGrupos extends StatelessWidget {
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: ChoiceChip(
|
||||
label: Text(
|
||||
l10n.favoriteGroupsChipLabel(
|
||||
l10n.favoritesFilterAllLabel,
|
||||
favoritos.length,
|
||||
),
|
||||
child: _chip(
|
||||
label: l10n.favoriteGroupsChipLabel(
|
||||
l10n.favoritesFilterAllLabel,
|
||||
favoritos.length,
|
||||
),
|
||||
selected: seleccionado == null,
|
||||
onSelected: (_) => onSeleccionar(null),
|
||||
onTap: () => onSeleccionar(null),
|
||||
),
|
||||
),
|
||||
for (final grupo in grupos)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 8),
|
||||
child: ChoiceChip(
|
||||
label: Text(
|
||||
l10n.favoriteGroupsChipLabel(
|
||||
_nombreVisible(l10n, grupo),
|
||||
favoritos
|
||||
.where((e) => e.grupoFavoritosId == grupo.id)
|
||||
.length,
|
||||
),
|
||||
child: _chip(
|
||||
label: l10n.favoriteGroupsChipLabel(
|
||||
_nombreVisible(l10n, grupo),
|
||||
favoritos.where((e) => e.grupoFavoritosId == grupo.id).length,
|
||||
),
|
||||
selected: seleccionado == grupo.id,
|
||||
onSelected: (_) => onSeleccionar(grupo.id),
|
||||
onTap: () => onSeleccionar(grupo.id),
|
||||
),
|
||||
),
|
||||
ActionChip(
|
||||
avatar: const Icon(Icons.add_rounded, size: 18),
|
||||
label: Text(l10n.favoriteGroupsManage),
|
||||
onPressed: onGestionar,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -357,7 +357,12 @@ void main() {
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
await tester.tap(find.text('Manage lists'));
|
||||
// Audit 4.1 (t4:216): the manage-groups action moved into the
|
||||
// header as a create_new_folder icon button, replacing the old
|
||||
// "Manage lists" ActionChip in the chip strip.
|
||||
await tester.tap(
|
||||
find.byKey(const ValueKey('favorites-manage-groups-action')),
|
||||
);
|
||||
await pumpStable(tester);
|
||||
|
||||
// "Group Management Reachable from Favoritos": the SAME screen
|
||||
@@ -385,7 +390,10 @@ void main() {
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.byType(PantallaAjustesGruposFavoritos), findsNothing);
|
||||
expect(find.text('Manage lists'), findsOneWidget);
|
||||
expect(
|
||||
find.byKey(const ValueKey('favorites-manage-groups-action')),
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
await estado.crearGrupoFavoritos('Road trip');
|
||||
await pumpStable(tester);
|
||||
@@ -494,6 +502,32 @@ void main() {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('visual fidelity (audit 4.2)', () {
|
||||
testWidgets('the active group chip is solid brand teal with dark text; '
|
||||
'inactive chips use listSurface (t4:219-221)', (tester) async {
|
||||
setLargeSurface(tester);
|
||||
_suppressListTileInkAssertion();
|
||||
final estado = await crearEstadoConFavoritos();
|
||||
addTearDown(estado.dispose);
|
||||
|
||||
await tester.pumpWidget(buildScreen(estado));
|
||||
await pumpStable(tester);
|
||||
|
||||
final activo = tester.widget<ChoiceChip>(
|
||||
find.widgetWithText(ChoiceChip, 'All · 3'),
|
||||
);
|
||||
expect(activo.selected, isTrue);
|
||||
expect(activo.selectedColor, const Color(0xFF21D4D9));
|
||||
expect(activo.labelStyle?.color, const Color(0xFF062126));
|
||||
|
||||
final inactivo = tester.widget<ChoiceChip>(
|
||||
find.widgetWithText(ChoiceChip, 'Rock · 2'),
|
||||
);
|
||||
expect(inactivo.selected, isFalse);
|
||||
expect(inactivo.backgroundColor, const Color(0xFF102532));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void setLargeSurface(WidgetTester tester) {
|
||||
|
||||
Reference in New Issue
Block a user