mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
fix: Add focusNode and textEditingController in Autocomplete (#170936)
<!-- Thanks for filing a pull request! Reviewers are typically assigned within a week of filing a request. To learn more about code review, see our documentation on Tree Hygiene: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md --> *Allows you to keep the selected value when the autocomplete is in a listview and you scroll through it.* ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Victor Sanni <victorsanniay@gmail.com>
This commit is contained in:
parent
5b6b62183f
commit
8aa701bf0c
@ -63,10 +63,12 @@ class Autocomplete<T extends Object> extends StatelessWidget {
|
||||
required this.optionsBuilder,
|
||||
this.displayStringForOption = RawAutocomplete.defaultStringForOption,
|
||||
this.fieldViewBuilder = _defaultFieldViewBuilder,
|
||||
this.focusNode,
|
||||
this.onSelected,
|
||||
this.optionsMaxHeight = 200.0,
|
||||
this.optionsViewBuilder,
|
||||
this.optionsViewOpenDirection = OptionsViewOpenDirection.down,
|
||||
this.textEditingController,
|
||||
this.initialValue,
|
||||
});
|
||||
|
||||
@ -79,6 +81,14 @@ class Autocomplete<T extends Object> extends StatelessWidget {
|
||||
/// default.
|
||||
final AutocompleteFieldViewBuilder fieldViewBuilder;
|
||||
|
||||
/// The [FocusNode] that is used for the text field.
|
||||
///
|
||||
/// {@macro flutter.widgets.RawAutocomplete.split}
|
||||
///
|
||||
/// If this parameter is not null, then [textEditingController] must also be
|
||||
/// non-null.
|
||||
final FocusNode? focusNode;
|
||||
|
||||
/// {@macro flutter.widgets.RawAutocomplete.onSelected}
|
||||
final AutocompleteOnSelected<T>? onSelected;
|
||||
|
||||
@ -102,6 +112,13 @@ class Autocomplete<T extends Object> extends StatelessWidget {
|
||||
/// The default value is set to 200.
|
||||
final double optionsMaxHeight;
|
||||
|
||||
/// The [TextEditingController] that is used for the text field.
|
||||
///
|
||||
/// {@macro flutter.widgets.RawAutocomplete.split}
|
||||
///
|
||||
/// If this parameter is not null, then [focusNode] must also be non-null.
|
||||
final TextEditingController? textEditingController;
|
||||
|
||||
/// {@macro flutter.widgets.RawAutocomplete.initialValue}
|
||||
final TextEditingValue? initialValue;
|
||||
|
||||
@ -123,6 +140,8 @@ class Autocomplete<T extends Object> extends StatelessWidget {
|
||||
return RawAutocomplete<T>(
|
||||
displayStringForOption: displayStringForOption,
|
||||
fieldViewBuilder: fieldViewBuilder,
|
||||
focusNode: focusNode,
|
||||
textEditingController: textEditingController,
|
||||
initialValue: initialValue,
|
||||
optionsBuilder: optionsBuilder,
|
||||
optionsViewOpenDirection: optionsViewOpenDirection,
|
||||
|
||||
@ -211,7 +211,7 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
|
||||
/// {@endtemplate}
|
||||
///
|
||||
/// If this parameter is not null, then [textEditingController] must also be
|
||||
/// not null.
|
||||
/// non-null.
|
||||
final FocusNode? focusNode;
|
||||
|
||||
/// {@template flutter.widgets.RawAutocomplete.optionsViewBuilder}
|
||||
@ -265,7 +265,7 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
|
||||
///
|
||||
/// {@macro flutter.widgets.RawAutocomplete.split}
|
||||
///
|
||||
/// If this parameter is not null, then [focusNode] must also be not null.
|
||||
/// If this parameter is not null, then [focusNode] must also be non-null.
|
||||
final TextEditingController? textEditingController;
|
||||
|
||||
/// {@template flutter.widgets.RawAutocomplete.initialValue}
|
||||
|
||||
@ -648,4 +648,87 @@ void main() {
|
||||
expect(optionFinder(kOptions.length - 1), findsNothing);
|
||||
checkOptionHighlight(tester, kOptions.first, highlightColor);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'passes textEditingController, focusNode to textEditingController, focusNode RawAutocomplete',
|
||||
(WidgetTester tester) async {
|
||||
final TextEditingController textEditingController = TextEditingController();
|
||||
final FocusNode focusNode = FocusNode();
|
||||
addTearDown(textEditingController.dispose);
|
||||
addTearDown(focusNode.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Material(
|
||||
child: Center(
|
||||
child: Autocomplete<String>(
|
||||
focusNode: focusNode,
|
||||
textEditingController: textEditingController,
|
||||
optionsBuilder: (TextEditingValue textEditingValue) => <String>['a'],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final RawAutocomplete<String> rawAutocomplete = tester.widget(
|
||||
find.byType(RawAutocomplete<String>),
|
||||
);
|
||||
expect(rawAutocomplete.textEditingController, textEditingController);
|
||||
expect(rawAutocomplete.focusNode, focusNode);
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('when field scrolled offscreen, reshown selected value when scrolled back', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
final TextEditingController textEditingController = TextEditingController();
|
||||
final FocusNode focusNode = FocusNode();
|
||||
addTearDown(textEditingController.dispose);
|
||||
addTearDown(focusNode.dispose);
|
||||
addTearDown(scrollController.dispose);
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: ListView(
|
||||
controller: scrollController,
|
||||
children: <Widget>[
|
||||
Autocomplete<String>(
|
||||
focusNode: focusNode,
|
||||
textEditingController: textEditingController,
|
||||
optionsBuilder: (TextEditingValue textEditingValue) {
|
||||
return kOptions.where((String option) {
|
||||
return option.contains(textEditingValue.text.toLowerCase());
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 1000.0),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
/// Select an option.
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pump();
|
||||
const String textSelection = 'chameleon';
|
||||
await tester.tap(find.text(textSelection));
|
||||
|
||||
// Unfocus and scroll to deconstruct the widge
|
||||
final TextField field = find.byType(TextField).evaluate().first.widget as TextField;
|
||||
field.focusNode?.unfocus();
|
||||
scrollController.jumpTo(2000.0);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
/// Scroll to go back to the widget.
|
||||
scrollController.jumpTo(0.0);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
/// Checks that the option selected is still present.
|
||||
final TextField field2 = find.byType(TextField).evaluate().first.widget as TextField;
|
||||
expect(field2.controller!.text, textSelection);
|
||||
});
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user