feat(core): let AppBanner disable its action instead of hiding it
Refs #1 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
86bcd25f30
commit
553a2b67cd
2 changed files with 42 additions and 1 deletions
|
|
@ -17,6 +17,7 @@ class AppBanner extends StatelessWidget {
|
|||
super.key,
|
||||
this.actionLabel,
|
||||
this.onAction,
|
||||
this.actionEnabled = true,
|
||||
}) : assert(
|
||||
(actionLabel == null) == (onAction == null),
|
||||
'actionLabel and onAction belong together',
|
||||
|
|
@ -28,6 +29,11 @@ class AppBanner extends StatelessWidget {
|
|||
final String? actionLabel;
|
||||
final VoidCallback? onAction;
|
||||
|
||||
/// `false` keeps the action visible and in the focus order but disabled
|
||||
/// (e.g. while a request runs). Screen readers still find it and hear that
|
||||
/// it is unavailable, instead of the control vanishing under their focus.
|
||||
final bool actionEnabled;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
|
@ -81,7 +87,7 @@ class AppBanner extends StatelessWidget {
|
|||
Align(
|
||||
alignment: AlignmentDirectional.centerEnd,
|
||||
child: TextButton(
|
||||
onPressed: onAction,
|
||||
onPressed: actionEnabled ? onAction : null,
|
||||
style: TextButton.styleFrom(foregroundColor: foreground),
|
||||
child: Text(actionLabel!),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:ui' show Tristate;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shuffle/core/widgets/app_banner.dart';
|
||||
|
|
@ -46,6 +48,39 @@ void main() {
|
|||
handle.dispose();
|
||||
});
|
||||
|
||||
testWidgets('a disabled action stays visible and announced as disabled', (
|
||||
tester,
|
||||
) async {
|
||||
final handle = tester.ensureSemantics();
|
||||
var calls = 0;
|
||||
await pumpWidgetUnderTest(
|
||||
tester,
|
||||
AppBanner(
|
||||
type: AppBannerType.info,
|
||||
typeLabel: 'Note',
|
||||
message: 'Confirm first',
|
||||
actionLabel: 'Resend',
|
||||
onAction: () => calls++,
|
||||
actionEnabled: false,
|
||||
),
|
||||
);
|
||||
|
||||
final action = find.widgetWithText(TextButton, 'Resend');
|
||||
expect(action, findsOneWidget);
|
||||
expect(tester.widget<TextButton>(action).onPressed, isNull);
|
||||
expect(
|
||||
tester
|
||||
.getSemantics(action)
|
||||
.getSemanticsData()
|
||||
.flagsCollection
|
||||
.isEnabled,
|
||||
Tristate.isFalse,
|
||||
);
|
||||
await tester.tap(action, warnIfMissed: false);
|
||||
expect(calls, 0);
|
||||
handle.dispose();
|
||||
});
|
||||
|
||||
testWidgets('action calls back and is at least 48 dp', (tester) async {
|
||||
var calls = 0;
|
||||
await pumpWidgetUnderTest(
|
||||
|
|
|
|||
Loading…
Reference in a new issue