Autocomplete Dropdown
Loading preview
Starting the Flutter app
A Flutter autocomplete dropdown that filters suggestions as you type.
import 'package:flutter/material.dart';
class AutocompleteDropDown extends StatefulWidget {
const AutocompleteDropDown({super.key});
@override
State<AutocompleteDropDown> createState() => _SimpleDropDownState();
}
class _SimpleDropDownState extends State<AutocompleteDropDown> {
String _selectedItem = '';
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: _formKey,
child: Center(
child: Container(
width: 300,
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Selected Country : $_selectedItem",
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 20),
AutocompleteTextField(
items: _countries,
decoration: const InputDecoration(
labelText: 'Select country',
border: OutlineInputBorder(),
),
// Only a picked suggestion counts, so typing a valid name
// without selecting it does not pass.
validator: (val) {
if (_selectedItem.isNotEmpty && val == _selectedItem) {
return null;
}
return 'Select a country from the list';
},
onItemSelect: (selected) {
setState(() {
_selectedItem = selected;
});
},
onChanged: (text) {
if (text != _selectedItem) {
setState(() {
_selectedItem = '';
});
}
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
String message = 'Form invalid';
if (_formKey.currentState?.validate() ?? false) {
message = 'Form valid';
}
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(message)));
},
child: const Text("Continue"),
),
],
),
),
),
),
);
}
}
/// A form field built on Flutter's [Autocomplete], which owns the text
/// controller, focus node and suggestions overlay.
class AutocompleteTextField extends StatelessWidget {
final List<String> items;
final ValueChanged<String> onItemSelect;
final ValueChanged<String>? onChanged;
final InputDecoration? decoration;
final String? Function(String?)? validator;
const AutocompleteTextField({
super.key,
required this.items,
required this.onItemSelect,
this.onChanged,
this.decoration,
this.validator,
});
@override
Widget build(BuildContext context) {
return Autocomplete<String>(
optionsBuilder: (value) {
final query = value.text.toLowerCase();
if (query.isEmpty) return items;
return items.where((item) => item.toLowerCase().contains(query));
},
onSelected: onItemSelect,
fieldViewBuilder: (context, controller, focusNode, onFieldSubmitted) {
return TextFormField(
controller: controller,
focusNode: focusNode,
decoration: decoration,
validator: validator,
onChanged: onChanged,
onFieldSubmitted: (_) => onFieldSubmitted(),
// Closes the suggestions so they can't sit over the Continue button.
onTapOutside: (_) => focusNode.unfocus(),
);
},
);
}
}
/// list of countries
final List<String> _countries = [
"Afghanistan",
"Åland Islands",
"Albania",
"Algeria",
"American Samoa",
"Andorra",
"Angola",
"Anguilla",
"Antarctica",
"Antigua and Barbuda",
"Argentina",
"Armenia",
"Aruba",
"Australia",
"Austria",
"Azerbaijan",
"Bahamas",
"Bahrain",
"Bangladesh",
"Barbados",
"Belarus",
"Belgium",
"Belize",
"Benin",
"Bermuda",
"Bhutan",
"Bolivia, Plurinational State of",
"Bonaire, Sint Eustatius and Saba",
"Bosnia and Herzegovina",
"Botswana",
"Bouvet Island",
"Brazil",
"British Indian Ocean Territory",
"Brunei Darussalam",
"Bulgaria",
"Burkina Faso",
"Burundi",
"Cambodia",
"Cameroon",
"Canada",
"Cape Verde",
"Cayman Islands",
"Central African Republic",
"Chad",
"Chile",
"China",
"Christmas Island",
"Cocos (Keeling) Islands",
"Colombia",
"Comoros",
"Congo",
"Congo, the Democratic Republic of the",
"Cook Islands",
"Costa Rica",
"Côte d'Ivoire",
"Croatia",
"Cuba",
"Curaçao",
"Cyprus",
"Czech Republic",
"Denmark",
"Djibouti",
"Dominica",
"Dominican Republic",
"Ecuador",
"Egypt",
"El Salvador",
"Equatorial Guinea",
"Eritrea",
"Estonia",
"Ethiopia",
"Falkland Islands (Malvinas)",
"Faroe Islands",
"Fiji",
"Finland",
"France",
"French Guiana",
"French Polynesia",
"French Southern Territories",
"Gabon",
"Gambia",
"Georgia",
"Germany",
"Ghana",
"Gibraltar",
"Greece",
"Greenland",
"Grenada",
"Guadeloupe",
"Guam",
"Guatemala",
"Guernsey",
"Guinea",
"Guinea-Bissau",
"Guyana",
"Haiti",
"Heard Island and McDonald Islands",
"Holy See (Vatican City State)",
"Honduras",
"Hong Kong",
"Hungary",
"Iceland",
"India",
"Indonesia",
"Iran, Islamic Republic of",
"Iraq",
"Ireland",
"Isle of Man",
"Israel",
"Italy",
"Jamaica",
"Japan",
"Jersey",
"Jordan",
"Kazakhstan",
"Kenya",
"Kiribati",
"Korea, Democratic People's Republic of",
"Korea, Republic of",
"Kuwait",
"Kyrgyzstan",
"Lao People's Democratic Republic",
"Latvia",
"Lebanon",
"Lesotho",
"Liberia",
"Libya",
"Liechtenstein",
"Lithuania",
"Luxembourg",
"Macao",
"Macedonia, the Former Yugoslav Republic of",
"Madagascar",
"Malawi",
"Malaysia",
"Maldives",
"Mali",
"Malta",
"Marshall Islands",
"Martinique",
"Mauritania",
"Mauritius",
"Mayotte",
"Mexico",
"Micronesia, Federated States of",
"Moldova, Republic of",
"Monaco",
"Mongolia",
"Montenegro",
"Montserrat",
"Morocco",
"Mozambique",
"Myanmar",
"Namibia",
"Nauru",
"Nepal",
"Netherlands",
"New Caledonia",
"New Zealand",
"Nicaragua",
"Niger",
"Nigeria",
"Niue",
"Norfolk Island",
"Northern Mariana Islands",
"Norway",
"Oman",
"Pakistan",
"Palau",
"Palestine, State of",
"Panama",
"Papua New Guinea",
"Paraguay",
"Peru",
"Philippines",
"Pitcairn",
"Poland",
"Portugal",
"Puerto Rico",
"Qatar",
"Réunion",
"Romania",
"Russian Federation",
"Rwanda",
"Saint Barthélemy",
"Saint Helena, Ascension and Tristan da Cunha",
"Saint Kitts and Nevis",
"Saint Lucia",
"Saint Martin (French part)",
"Saint Pierre and Miquelon",
"Saint Vincent and the Grenadines",
"Samoa",
"San Marino",
"Sao Tome and Principe",
"Saudi Arabia",
"Senegal",
"Serbia",
"Seychelles",
"Sierra Leone",
"Singapore",
"Sint Maarten (Dutch part)",
"Slovakia",
"Slovenia",
"Solomon Islands",
"Somalia",
"South Africa",
"South Georgia and the South Sandwich Islands",
"South Sudan",
"Spain",
"Sri Lanka",
"Sudan",
"Suriname",
"Svalbard and Jan Mayen",
"Swaziland",
"Sweden",
"Switzerland",
"Syrian Arab Republic",
"Taiwan, Province of China",
"Tajikistan",
"Tanzania, United Republic of",
"Thailand",
"Timor-Leste",
"Togo",
"Tokelau",
"Tonga",
"Trinidad and Tobago",
"Tunisia",
"Turkey",
"Turkmenistan",
"Turks and Caicos Islands",
"Tuvalu",
"Uganda",
"Ukraine",
"United Arab Emirates",
"United Kingdom",
"United States",
"United States Minor Outlying Islands",
"Uruguay",
"Uzbekistan",
"Vanuatu",
"Venezuela, Bolivarian Republic of",
"Viet Nam",
"Virgin Islands, British",
"Virgin Islands, U.S.",
"Wallis and Futuna",
"Western Sahara",
"Yemen",
"Zambia",
"Zimbabwe",
];
Add it to your project
Copies the Dart source into your app, so you own it. Flutter SDK only, no extra packages.
First time? Run dart pub global activate fluttertemplates_dev, or read the CLI guide.
How it works
AutocompleteTextField wraps Flutter’s Autocomplete, which owns the controller, focus node and suggestions overlay, so there’s nothing to dispose here. optionsBuilder filters items by substring on every keystroke, and fieldViewBuilder supplies a TextFormField so the field joins the Form. Validation is selection-only: onItemSelect sets _selectedItem, onChanged clears it when the text diverges, and a typed country fails until it’s picked from the list.
Adapting it
- Replace
_countrieswith your own options, or fetch them insideoptionsBuilder. - Style the list with
optionsViewBuilder. - Use
displayStringForOptionif your options are typed models.