Unverified Commit a714c97c authored by Darren Austin's avatar Darren Austin Committed by GitHub

Ensure the Autocomplete options scroll as needed. (#83863)

parent 4705e0cc
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/scheduler.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'ink_well.dart'; import 'ink_well.dart';
...@@ -292,15 +293,24 @@ class _AutocompleteOptions<T extends Object> extends StatelessWidget { ...@@ -292,15 +293,24 @@ class _AutocompleteOptions<T extends Object> extends StatelessWidget {
itemCount: options.length, itemCount: options.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final T option = options.elementAt(index); final T option = options.elementAt(index);
final bool highlight = AutocompleteHighlightedOption.of(context) == index;
return InkWell( return InkWell(
onTap: () { onTap: () {
onSelected(option); onSelected(option);
}, },
child: Container( child: Builder(
builder: (BuildContext context) {
final bool highlight = AutocompleteHighlightedOption.of(context) == index;
if (highlight) {
SchedulerBinding.instance!.addPostFrameCallback((Duration timeStamp) {
Scrollable.ensureVisible(context, alignment: 0.5);
});
}
return Container(
color: highlight ? Theme.of(context).focusColor : null, color: highlight ? Theme.of(context).focusColor : null,
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
child: Text(displayStringForOption(option)), child: Text(displayStringForOption(option)),
);
}
), ),
); );
}, },
......
...@@ -402,9 +402,9 @@ void main() { ...@@ -402,9 +402,9 @@ void main() {
expect(lastSelection, 'lemur'); expect(lastSelection, 'lemur');
}); });
testWidgets('keyboard navigation of the options properly highlights the option', (WidgetTester tester) async { // Ensures that the option with the given label has a given background color
// if given, or no background if color is null.
void checkOptionHighlight(String label, Color? color) { void checkOptionHighlight(WidgetTester tester, String label, Color? color) {
final RenderBox renderBox = tester.renderObject<RenderBox>(find.ancestor(matching: find.byType(Container), of: find.text(label))); final RenderBox renderBox = tester.renderObject<RenderBox>(find.ancestor(matching: find.byType(Container), of: find.text(label)));
if (color != null) { if (color != null) {
// Check to see that the container is painted with the highlighted background color. // Check to see that the container is painted with the highlighted background color.
...@@ -416,6 +416,7 @@ void main() { ...@@ -416,6 +416,7 @@ void main() {
} }
} }
testWidgets('keyboard navigation of the options properly highlights the option', (WidgetTester tester) async {
const Color highlightColor = Color(0xFF112233); const Color highlightColor = Color(0xFF112233);
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
...@@ -442,15 +443,68 @@ void main() { ...@@ -442,15 +443,68 @@ void main() {
expect(list.semanticChildCount, 2); expect(list.semanticChildCount, 2);
// Initially the first option should be highlighted // Initially the first option should be highlighted
checkOptionHighlight('chameleon', highlightColor); checkOptionHighlight(tester, 'chameleon', highlightColor);
checkOptionHighlight('elephant', null); checkOptionHighlight(tester, 'elephant', null);
// Move the selection down // Move the selection down
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.pump(); await tester.pump();
// Highlight should be moved to the second item // Highlight should be moved to the second item
checkOptionHighlight('chameleon', null); checkOptionHighlight(tester, 'chameleon', null);
checkOptionHighlight('elephant', highlightColor); checkOptionHighlight(tester, 'elephant', highlightColor);
});
testWidgets('keyboard navigation keeps the highlighted option scrolled into view', (WidgetTester tester) async {
const Color highlightColor = Color(0xFF112233);
await tester.pumpWidget(
MaterialApp(
theme: ThemeData.light().copyWith(
focusColor: highlightColor,
),
home: Scaffold(
body: Autocomplete<String>(
optionsBuilder: (TextEditingValue textEditingValue) {
return kOptions.where((String option) {
return option.contains(textEditingValue.text.toLowerCase());
});
},
),
),
),
);
await tester.tap(find.byType(TextFormField));
await tester.enterText(find.byType(TextFormField), 'e');
await tester.pump();
expect(find.byType(ListView), findsOneWidget);
final ListView list = find.byType(ListView).evaluate().first.widget as ListView;
expect(list.semanticChildCount, 6);
// Highlighted item should be at the top
expect(tester.getTopLeft(find.text('chameleon')).dy, equals(64.0));
checkOptionHighlight(tester, 'chameleon', highlightColor);
// Move down the list of options
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown);
await tester.pump();
// First item should have scrolled off the top, and not be selected.
expect(find.text('chameleon'), findsNothing);
// Highlighted item 'lemur' should be centered in the options popup
expect(tester.getTopLeft(find.text('mouse')).dy, equals(187.0));
checkOptionHighlight(tester, 'mouse', highlightColor);
// The other items on screen should not be selected.
checkOptionHighlight(tester, 'goose', null);
checkOptionHighlight(tester, 'lemur', null);
checkOptionHighlight(tester, 'northern white rhinoceros', null);
}); });
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment