flutter_typeahead 1.3.0 flutter_typeahead: ^1.3.0 copied to clipboard
A highly customizable typeahead (autocomplete) text input field for Flutter
import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'flutter_typeahead demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 3,
child: Scaffold(
appBar: AppBar(
title: TabBar(tabs: [
Tab(
text: 'Example 1: Navigation',
),
Tab(text: 'Example 2: Form'),
Tab(text: 'Example 3: Scroll')
]),
),
body: TabBarView(children: [
NavigationExample(),
FormExample(),
ScrollExample(),
])),
);
}
}
class NavigationExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.all(32.0),
child: Column(
children: <Widget>[
SizedBox(
height: 10.0,
),
TypeAheadField(
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
style: DefaultTextStyle.of(context)
.style
.copyWith(fontStyle: FontStyle.italic),
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'What are you looking for?'),
),
suggestionsCallback: (pattern) async {
return await BackendService.getSuggestions(pattern);
},
itemBuilder: (context, suggestion) {
return ListTile(
leading: Icon(Icons.shopping_cart),
title: Text(suggestion['name']),
subtitle: Text('\$${suggestion['price']}'),
);
},
onSuggestionSelected: (suggestion) {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ProductPage(product: suggestion)));
},
),
],
),
);
}
}
class FormExample extends StatefulWidget {
@override
_FormExampleState createState() => _FormExampleState();
}
class _FormExampleState extends State<FormExample> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _typeAheadController = TextEditingController();
String _selectedCity;
@override
Widget build(BuildContext context) {
return Form(
key: this._formKey,
child: Padding(
padding: EdgeInsets.all(32.0),
child: Column(
children: <Widget>[
Text('What is your favorite city?'),
TypeAheadFormField(
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(labelText: 'City'),
controller: this._typeAheadController,
),
suggestionsCallback: (pattern) {
return CitiesService.getSuggestions(pattern);
},
itemBuilder: (context, suggestion) {
return ListTile(
title: Text(suggestion),
);
},
transitionBuilder: (context, suggestionsBox, controller) {
return suggestionsBox;
},
onSuggestionSelected: (suggestion) {
this._typeAheadController.text = suggestion;
},
validator: (value) {
if (value.isEmpty) {
return 'Please select a city';
}
},
onSaved: (value) => this._selectedCity = value,
),
SizedBox(
height: 10.0,
),
RaisedButton(
child: Text('Submit'),
onPressed: () {
if (this._formKey.currentState.validate()) {
this._formKey.currentState.save();
Scaffold.of(context).showSnackBar(SnackBar(
content:
Text('Your Favorite City is ${this._selectedCity}')));
}
},
)
],
),
),
);
}
}
class ScrollExample extends StatelessWidget {
final List<String> items = List.generate(5, (index) => "Item $index");
@override
Widget build(BuildContext context) {
return ListView(children: [
Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text("Suggestion box will resize when scrolling"),
)),
SizedBox(height: 200),
TypeAheadField<String>(
getImmediateSuggestions: true,
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'What are you looking for?'),
),
suggestionsCallback: (String pattern) async {
return items
.where((item) =>
item.toLowerCase().startsWith(pattern.toLowerCase()))
.toList();
},
itemBuilder: (context, String suggestion) {
return ListTile(
title: Text(suggestion),
);
},
onSuggestionSelected: (String suggestion) {
print("Suggestion selected");
},
),
SizedBox(height: 500),
]);
}
}
class ProductPage extends StatelessWidget {
final Map<String, dynamic> product;
ProductPage({this.product});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(50.0),
child: Column(
children: [
Text(
this.product['name'],
style: Theme.of(context).textTheme.headline,
),
Text(
this.product['price'].toString() + ' USD',
style: Theme.of(context).textTheme.subhead,
)
],
),
),
);
}
}
class BackendService {
static Future<List> getSuggestions(String query) async {
await Future.delayed(Duration(seconds: 1));
return List.generate(3, (index) {
return {'name': query + index.toString(), 'price': Random().nextInt(100)};
});
}
}
class CitiesService {
static final cities = [
'Beirut',
'Damascus',
'San Fransisco',
'Rome',
'Los Angeles',
'Madrid',
'Bali',
'Barcelona',
'Paris',
'Bucharest'
];
static getSuggestions(String query) {
final cities = CitiesService.cities;
cities.sort((a, b) {
return CitiesService._distance(query, a) -
CitiesService._distance(query, b);
});
return cities.take(4).toList();
}
// source: https://github.com/kseo/edit_distance/blob/master/lib/src/levenshtein.dart
static int _distance(String s1, String s2) {
if (s1 == s2) {
return 0;
}
if (s1.length == 0) {
return s2.length;
}
if (s2.length == 0) {
return s1.length;
}
List<int> v0 = new List<int>(s2.length + 1);
List<int> v1 = new List<int>(s2.length + 1);
List<int> vtemp;
for (var i = 0; i < v0.length; i++) {
v0[i] = i;
}
for (var i = 0; i < s1.length; i++) {
v1[0] = i + 1;
for (var j = 0; j < s2.length; j++) {
int cost = 1;
if (s1.codeUnitAt(i) == s2.codeUnitAt(j)) {
cost = 0;
}
v1[j + 1] = min(v1[j] + 1, min(v0[j + 1] + 1, v0[j] + cost));
}
vtemp = v0;
v0 = v1;
v1 = vtemp;
}
return v0[s2.length];
}
}