// 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/widgets.dart'; import 'ink_well.dart'; import 'material.dart'; import 'text_form_field.dart'; /// {@macro flutter.widgets.RawAutocomplete.RawAutocomplete} /// /// {@tool dartpad --template=freeform} /// This example shows how to create a very basic Autocomplete widget using the /// default UI. /// /// ```dart imports /// import 'package:flutter/material.dart'; /// ``` /// /// ```dart /// class AutocompleteBasicExample extends StatelessWidget { /// AutocompleteBasicExample({Key? key}) : super(key: key); /// /// final List _kOptions = [ /// 'aardvark', /// 'bobcat', /// 'chameleon', /// ]; /// /// @override /// Widget build(BuildContext context) { /// return Autocomplete( /// optionsBuilder: (TextEditingValue textEditingValue) { /// if (textEditingValue.text == '') { /// return const Iterable.empty(); /// } /// return _kOptions.where((String option) { /// return option.contains(textEditingValue.text.toLowerCase()); /// }); /// }, /// onSelected: (String selection) { /// print('You just selected $selection'); /// }, /// ); /// } /// } /// ``` /// {@end-tool} /// /// {@tool dartpad --template=freeform} /// This example shows how to create an Autocomplete widget with a custom type. /// Try searching with text from the name or email field. /// /// ```dart imports /// import 'package:flutter/material.dart'; /// ``` /// /// ```dart /// class User { /// const User({ /// required this.email, /// required this.name, /// }); /// /// final String email; /// final String name; /// /// @override /// String toString() { /// return '$name, $email'; /// } /// /// @override /// bool operator ==(Object other) { /// if (other.runtimeType != runtimeType) /// return false; /// return other is User /// && other.name == name /// && other.email == email; /// } /// /// @override /// int get hashCode => hashValues(email, name); /// } /// /// class AutocompleteBasicUserExample extends StatelessWidget { /// AutocompleteBasicUserExample({Key? key}) : super(key: key); /// /// static final List _userOptions = [ /// User(name: 'Alice', email: 'alice@example.com'), /// User(name: 'Bob', email: 'bob@example.com'), /// User(name: 'Charlie', email: 'charlie123@gmail.com'), /// ]; /// /// static String _displayStringForOption(User option) => option.name; /// /// @override /// Widget build(BuildContext context) { /// return Autocomplete( /// displayStringForOption: _displayStringForOption, /// optionsBuilder: (TextEditingValue textEditingValue) { /// if (textEditingValue.text == '') { /// return const Iterable.empty(); /// } /// return _userOptions.where((User option) { /// return option.toString().contains(textEditingValue.text.toLowerCase()); /// }); /// }, /// onSelected: (User selection) { /// print('You just selected ${_displayStringForOption(selection)}'); /// }, /// ); /// } /// } /// ``` /// {@end-tool} /// /// See also: /// /// * [RawAutocomplete], which is what Autocomplete is built upon, and which /// contains more detailed examples. class Autocomplete extends StatelessWidget { /// Creates an instance of [Autocomplete]. const Autocomplete({ Key? key, required this.optionsBuilder, this.displayStringForOption = RawAutocomplete.defaultStringForOption, this.fieldViewBuilder = _defaultFieldViewBuilder, this.onSelected, this.optionsViewBuilder, }) : assert(displayStringForOption != null), assert(optionsBuilder != null), super(key: key); /// {@macro flutter.widgets.RawAutocomplete.displayStringForOption} final AutocompleteOptionToString 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? onSelected; /// {@macro flutter.widgets.RawAutocomplete.optionsBuilder} final AutocompleteOptionsBuilder optionsBuilder; /// {@macro flutter.widgets.RawAutocomplete.optionsViewBuilder} /// /// If not provided, will build a standard Material-style list of results by /// default. final AutocompleteOptionsViewBuilder? optionsViewBuilder; 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( displayStringForOption: displayStringForOption, fieldViewBuilder: fieldViewBuilder, optionsBuilder: optionsBuilder, optionsViewBuilder: optionsViewBuilder ?? (BuildContext context, AutocompleteOnSelected onSelected, Iterable options) { return _AutocompleteOptions( displayStringForOption: displayStringForOption, onSelected: onSelected, options: options, ); }, onSelected: onSelected, ); } } // The default Material-style Autocomplete text field. class _AutocompleteField extends StatelessWidget { const _AutocompleteField({ Key? key, required this.focusNode, required this.textEditingController, required this.onFieldSubmitted, }) : super(key: key); 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 extends StatelessWidget { const _AutocompleteOptions({ Key? key, required this.displayStringForOption, required this.onSelected, required this.options, }) : super(key: key); final AutocompleteOptionToString displayStringForOption; final AutocompleteOnSelected onSelected; final Iterable options; @override Widget build(BuildContext context) { return Align( alignment: Alignment.topLeft, child: Material( elevation: 4.0, child: Container( height: 200.0, child: ListView.builder( padding: EdgeInsets.zero, itemCount: options.length, itemBuilder: (BuildContext context, int index) { final T option = options.elementAt(index); return InkWell( onTap: () { onSelected(option); }, child: Padding( padding: const EdgeInsets.all(16.0), child: Text(displayStringForOption(option)), ), ); }, ), ), ), ); } }