| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/scheduler.dart'; |
| import 'package:flutter/widgets.dart'; |
| |
| import 'ink_well.dart'; |
| import 'material.dart'; |
| import 'text_form_field.dart'; |
| import 'theme.dart'; |
| |
| /// {@macro flutter.widgets.RawAutocomplete.RawAutocomplete} |
| /// |
| /// {@youtube 560 315 https://www.youtube.com/watch?v=-Nny8kzW380} |
| /// |
| /// {@tool dartpad} |
| /// This example shows how to create a very basic Autocomplete widget using the |
| /// default UI. |
| /// |
| /// ** See code in examples/api/lib/material/autocomplete/autocomplete.0.dart ** |
| /// {@end-tool} |
| /// |
| /// {@tool dartpad} |
| /// This example shows how to create an Autocomplete widget with a custom type. |
| /// Try searching with text from the name or email field. |
| /// |
| /// ** See code in examples/api/lib/material/autocomplete/autocomplete.1.dart ** |
| /// {@end-tool} |
| /// |
| /// {@tool dartpad} |
| /// This example shows how to create an Autocomplete widget whose options are |
| /// fetched over the network. |
| /// |
| /// ** See code in examples/api/lib/material/autocomplete/autocomplete.2.dart ** |
| /// {@end-tool} |
| /// |
| /// {@tool dartpad} |
| /// This example shows how to create an Autocomplete widget whose options are |
| /// fetched over the network. It uses debouncing to wait to perform the network |
| /// request until after the user finishes typing. |
| /// |
| /// ** See code in examples/api/lib/material/autocomplete/autocomplete.3.dart ** |
| /// {@end-tool} |
| /// |
| /// {@tool dartpad} |
| /// This example shows how to create an Autocomplete widget whose options are |
| /// fetched over the network. It includes both debouncing and error handling, so |
| /// that failed network requests show an error to the user and can be recovered |
| /// from. Try toggling the network Switch widget to simulate going offline. |
| /// |
| /// ** See code in examples/api/lib/material/autocomplete/autocomplete.4.dart ** |
| /// {@end-tool} |
| /// |
| /// See also: |
| /// |
| /// * [RawAutocomplete], which is what Autocomplete is built upon, and which |
| /// contains more detailed examples. |
| class Autocomplete<T extends Object> extends StatelessWidget { |
| /// Creates an instance of [Autocomplete]. |
| const Autocomplete({ |
| super.key, |
| required this.optionsBuilder, |
| this.displayStringForOption = RawAutocomplete.defaultStringForOption, |
| this.fieldViewBuilder = _defaultFieldViewBuilder, |
| this.onSelected, |
| this.optionsMaxHeight = 200.0, |
| this.optionsViewBuilder, |
| this.optionsViewOpenDirection = OptionsViewOpenDirection.down, |
| this.initialValue, |
| }); |
| |
| /// {@macro flutter.widgets.RawAutocomplete.displayStringForOption} |
| final AutocompleteOptionToString<T> displayStringForOption; |
| |
| /// {@macro flutter.widgets.RawAutocomplete.fieldViewBuilder} |
| /// |
| /// If not provided, will build a standard Material-style text field by |
| /// default. |
| final AutocompleteFieldViewBuilder fieldViewBuilder; |
| |
| /// {@macro flutter.widgets.RawAutocomplete.onSelected} |
| final AutocompleteOnSelected<T>? onSelected; |
| |
| /// {@macro flutter.widgets.RawAutocomplete.optionsBuilder} |
| final AutocompleteOptionsBuilder<T> optionsBuilder; |
| |
| /// {@macro flutter.widgets.RawAutocomplete.optionsViewBuilder} |
| /// |
| /// If not provided, will build a standard Material-style list of results by |
| /// default. |
| final AutocompleteOptionsViewBuilder<T>? optionsViewBuilder; |
| |
| /// {@macro flutter.widgets.RawAutocomplete.optionsViewOpenDirection} |
| final OptionsViewOpenDirection optionsViewOpenDirection; |
| |
| /// The maximum height used for the default Material options list widget. |
| /// |
| /// When [optionsViewBuilder] is `null`, this property sets the maximum height |
| /// that the options widget can occupy. |
| /// |
| /// The default value is set to 200. |
| final double optionsMaxHeight; |
| |
| /// {@macro flutter.widgets.RawAutocomplete.initialValue} |
| final TextEditingValue? initialValue; |
| |
| static Widget _defaultFieldViewBuilder(BuildContext context, TextEditingController textEditingController, FocusNode focusNode, VoidCallback onFieldSubmitted) { |
| return _AutocompleteField( |
| focusNode: focusNode, |
| textEditingController: textEditingController, |
| onFieldSubmitted: onFieldSubmitted, |
| ); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return RawAutocomplete<T>( |
| displayStringForOption: displayStringForOption, |
| fieldViewBuilder: fieldViewBuilder, |
| initialValue: initialValue, |
| optionsBuilder: optionsBuilder, |
| optionsViewOpenDirection: optionsViewOpenDirection, |
| optionsViewBuilder: optionsViewBuilder ?? (BuildContext context, AutocompleteOnSelected<T> onSelected, Iterable<T> options) { |
| return _AutocompleteOptions<T>( |
| displayStringForOption: displayStringForOption, |
| onSelected: onSelected, |
| options: options, |
| maxOptionsHeight: optionsMaxHeight, |
| ); |
| }, |
| onSelected: onSelected, |
| ); |
| } |
| } |
| |
| // The default Material-style Autocomplete text field. |
| class _AutocompleteField extends StatelessWidget { |
| const _AutocompleteField({ |
| required this.focusNode, |
| required this.textEditingController, |
| required this.onFieldSubmitted, |
| }); |
| |
| final FocusNode focusNode; |
| |
| final VoidCallback onFieldSubmitted; |
| |
| final TextEditingController textEditingController; |
| |
| @override |
| Widget build(BuildContext context) { |
| return TextFormField( |
| controller: textEditingController, |
| focusNode: focusNode, |
| onFieldSubmitted: (String value) { |
| onFieldSubmitted(); |
| }, |
| ); |
| } |
| } |
| |
| // The default Material-style Autocomplete options. |
| class _AutocompleteOptions<T extends Object> extends StatelessWidget { |
| const _AutocompleteOptions({ |
| super.key, |
| required this.displayStringForOption, |
| required this.onSelected, |
| required this.options, |
| required this.maxOptionsHeight, |
| }); |
| |
| final AutocompleteOptionToString<T> displayStringForOption; |
| |
| final AutocompleteOnSelected<T> onSelected; |
| |
| final Iterable<T> options; |
| final double maxOptionsHeight; |
| |
| @override |
| Widget build(BuildContext context) { |
| return Align( |
| alignment: Alignment.topLeft, |
| child: Material( |
| elevation: 4.0, |
| child: ConstrainedBox( |
| constraints: BoxConstraints(maxHeight: maxOptionsHeight), |
| child: ListView.builder( |
| padding: EdgeInsets.zero, |
| shrinkWrap: true, |
| itemCount: options.length, |
| itemBuilder: (BuildContext context, int index) { |
| final T option = options.elementAt(index); |
| return InkWell( |
| onTap: () { |
| onSelected(option); |
| }, |
| 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, |
| padding: const EdgeInsets.all(16.0), |
| child: Text(displayStringForOption(option)), |
| ); |
| } |
| ), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ); |
| } |
| } |