fix(alarmas): restyle the editor sheet as the prototype draws it

Audit 8.1-8.4: opaque bottom-anchored sheet with a grab handle, a framed
time card and 7 circular day buttons.

The date field, fallback-station picker and sound dropdown all stay
reachable -- the prototype omits them, but presentation changes never
remove capability.
This commit is contained in:
2026-07-30 10:08:54 +02:00
parent 653848899f
commit 2255374291
2 changed files with 250 additions and 28 deletions
@@ -6,6 +6,7 @@ import 'package:pluriwave/l10n/gen/app_localizations.dart';
import 'package:pluriwave/modelos/alarma_musical.dart';
import 'package:pluriwave/pantallas/pantalla_alarmas.dart';
import 'package:pluriwave/servicios/servicio_alarmas.dart';
import 'package:pluriwave/widgets/editor_hora_inline.dart';
import 'package:pluriwave/widgets/pluri_push_scaffold.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -294,6 +295,113 @@ void main() {
expect(volumen.min, 0.0);
});
group('visual fidelity (audit 8.1/8.3/8.4, item 20)', () {
testWidgets(
'8.1: the sheet is an opaque, bottom-anchored surface (radius on the '
'top corners only) with a grab handle',
(tester) async {
await _abrirEditor(tester);
final surface = tester.widget<DecoratedBox>(
find.byKey(const ValueKey('alarm-editor-sheet-surface')),
);
final decoracion = surface.decoration as BoxDecoration;
expect(
decoracion.color,
const Color(0xFF0D1B24),
reason: 't4 line 373: background:#0D1B24 (opaque, not glass)',
);
expect(
decoracion.borderRadius,
const BorderRadius.vertical(top: Radius.circular(30)),
reason:
't4 line 373: border-radius:30px 30px 0 0 — top corners '
'only, unlike a floating dialog',
);
final asa = tester.widget<Container>(
find.byKey(const ValueKey('alarm-editor-grab-handle')),
);
expect(asa.constraints?.maxWidth, 44);
expect(asa.constraints?.maxHeight, 4);
final asaDecoracion = asa.decoration as BoxDecoration;
expect(
asaDecoracion.borderRadius,
BorderRadius.circular(2),
reason: 't4 line 374: width:44px;height:4px;border-radius:2px',
);
},
);
testWidgets('8.3: the hour editor sits inside a framed card', (
tester,
) async {
await _abrirEditor(tester);
final tarjeta = tester.widget<DecoratedBox>(
find.ancestor(
of: find.byType(EditorHoraInline),
matching: find.byKey(const ValueKey('alarm-editor-hour-card')),
),
);
final decoracion = tarjeta.decoration as BoxDecoration;
expect(
decoracion.borderRadius,
BorderRadius.circular(22),
reason: 't4 line 378: border-radius:22px',
);
expect(
decoracion.color,
const Color(0xFF102532),
reason: 't4 line 378: background:#102532 (== listSurface token)',
);
});
testWidgets(
'8.4: the weekday selector is exactly 7 circular buttons, not chips',
(tester) async {
await _abrirEditor(tester);
final fila = find.byKey(const ValueKey('alarm-weekday-circles'));
expect(fila, findsOneWidget);
expect(
find.descendant(of: fila, matching: find.byType(FilterChip)),
findsNothing,
reason: 't4 lines 383-390: 7 circles, not FilterChips',
);
final circulos = find.descendant(
of: fila,
matching: find.byWidgetPredicate(
(w) => w is Material && w.shape is CircleBorder,
),
);
expect(circulos, findsNWidgets(7));
},
);
testWidgets(
'capacity guard: the date field, the fallback-station picker and the '
'sound dropdown all stay reachable behind Advanced',
(tester) async {
await _abrirEditor(tester);
await tester.ensureVisible(find.text(l10n.alarmAdvancedSectionTitle));
await tester.tap(find.text(l10n.alarmAdvancedSectionTitle));
await tester.pumpAndSettle();
expect(find.text(l10n.dateField), findsOneWidget);
expect(
find.byKey(const ValueKey('alarm-fallback-station-field')),
findsOneWidget,
);
expect(
find.byType(DropdownButtonFormField<SonidoInternoAlarma>),
findsOneWidget,
);
},
);
});
group('WU8 — tarjeta de alarma simplificada', () {
testWidgets(
'tocar la tarjeta abre el editor precargado con los datos de esa '