refactor material banner and fix focus issue (#152646)

- Refactored material banner setup to make it more simple and stateless
- Added focusNode to where the dismiss button focuses automatically when banner is shown

Before: https://screencast.googleplex.com/cast/NTI4OTMxNjU2MDQwNDQ4MHw2ODdiMmYwOC1hMg
After: https://screencast.googleplex.com/cast/NjAzNTY4NDA2NTI4MDAwMHwyNTRjMWUxMi0zMA

fixes b/346646604
This commit is contained in:
Denis Bowen 2024-08-19 18:11:58 -05:00 committed by GitHub
parent 767bfd31da
commit d528c7903f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 57 additions and 30 deletions

View File

@ -25,38 +25,43 @@ class MainWidget extends StatefulWidget {
}
class MainWidgetState extends State<MainWidget> {
double currentSliderValue = 20;
ScaffoldFeatureController<MaterialBanner, MaterialBannerClosedReason>?
controller;
final FocusNode dismissButtonFocusNode = FocusNode();
final FocusNode showButtonFocusNode = FocusNode();
@override
void dispose() {
dismissButtonFocusNode.dispose();
showButtonFocusNode.dispose();
super.dispose();
}
void hideBanner() {
ScaffoldMessenger.of(context).hideCurrentMaterialBanner();
showButtonFocusNode.requestFocus();
}
void showBanner() {
ScaffoldMessenger.of(context).showMaterialBanner(
MaterialBanner(
padding: const EdgeInsets.all(20),
content: const Text('Hello, I am a Material Banner'),
leading: const Icon(Icons.agriculture_outlined),
backgroundColor: Colors.yellowAccent,
actions: <Widget>[
TextButton(
focusNode: dismissButtonFocusNode,
onPressed: hideBanner,
child: const Text('DISMISS'),
),
],
),
);
dismissButtonFocusNode.requestFocus();
}
@override
Widget build(BuildContext context) {
VoidCallback? onPress;
if (controller == null) {
onPress = () {
setState(() {
controller = ScaffoldMessenger.of(context).showMaterialBanner(
MaterialBanner(
padding: const EdgeInsets.all(20),
content: const Text('Hello, I am a Material Banner'),
leading: const Icon(Icons.agriculture_outlined),
backgroundColor: Colors.green,
actions: <Widget>[
TextButton(
onPressed: () {
controller!.close();
setState(() {
controller = null;
});
},
child: const Text('DISMISS'),
),
],
),
);
});
};
}
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
@ -64,7 +69,8 @@ class MainWidgetState extends State<MainWidget> {
),
body: Center(
child: ElevatedButton(
onPressed: onPress,
focusNode: showButtonFocusNode,
onPressed: showBanner,
child: const Text('Show a MaterialBanner'),
),
),

View File

@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:a11y_assessments/use_cases/material_banner.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'test_utils.dart';
@ -21,6 +22,26 @@ void main() {
expect(find.text('Hello, I am a Material Banner'), findsNothing);
});
testWidgets('dismiss button focused on banner open', (WidgetTester tester) async {
await pumpsUseCase(tester, MaterialBannerUseCase());
await tester.tap(find.text('Show a MaterialBanner'));
await tester.pumpAndSettle();
final TextButton dismissButtonFinder = tester.widget<TextButton>(find.byType(TextButton));
expect(dismissButtonFinder.focusNode!.hasFocus, isTrue);
});
testWidgets('show button focused on banner close', (WidgetTester tester) async {
await pumpsUseCase(tester, MaterialBannerUseCase());
await tester.tap(find.text('Show a MaterialBanner'));
await tester.pumpAndSettle();
await tester.tap(find.byType(TextButton));
final ElevatedButton showButtonFinder = tester.widget<ElevatedButton>(find.byType(ElevatedButton));
expect(showButtonFinder.focusNode!.hasFocus, isTrue);
});
testWidgets('material banner has one h1 tag', (WidgetTester tester) async {
await pumpsUseCase(tester, MaterialBannerUseCase());
final Finder findHeadingLevelOnes = find.bySemanticsLabel('MaterialBanner Demo');