1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | import 'package:flutter/material.dart'; |
6 | |
7 | /// Flutter code sample for [RawAutocomplete]. |
8 | |
9 | void main() => runApp(const AutocompleteExampleApp()); |
10 | |
11 | class AutocompleteExampleApp extends StatelessWidget { |
12 | const AutocompleteExampleApp({super.key}); |
13 | |
14 | @override |
15 | Widget build(BuildContext context) { |
16 | return MaterialApp( |
17 | home: Scaffold( |
18 | appBar: AppBar( |
19 | title: const Text('RawAutocomplete Custom Type' ), |
20 | ), |
21 | body: const Center( |
22 | child: AutocompleteCustomTypeExample(), |
23 | ), |
24 | ), |
25 | ); |
26 | } |
27 | } |
28 | |
29 | // An example of a type that someone might want to autocomplete a list of. |
30 | @immutable |
31 | class User { |
32 | const User({ |
33 | required this.email, |
34 | required this.name, |
35 | }); |
36 | |
37 | final String email; |
38 | final String name; |
39 | |
40 | @override |
41 | String toString() { |
42 | return ' $name, $email' ; |
43 | } |
44 | |
45 | @override |
46 | bool operator ==(Object other) { |
47 | if (other.runtimeType != runtimeType) { |
48 | return false; |
49 | } |
50 | return other is User && other.name == name && other.email == email; |
51 | } |
52 | |
53 | @override |
54 | int get hashCode => Object.hash(email, name); |
55 | } |
56 | |
57 | class AutocompleteCustomTypeExample extends StatelessWidget { |
58 | const AutocompleteCustomTypeExample({super.key}); |
59 | |
60 | static const List<User> _userOptions = <User>[ |
61 | User(name: 'Alice' , email: 'alice@example.com' ), |
62 | User(name: 'Bob' , email: 'bob@example.com' ), |
63 | User(name: 'Charlie' , email: 'charlie123@gmail.com' ), |
64 | ]; |
65 | |
66 | static String _displayStringForOption(User option) => option.name; |
67 | |
68 | @override |
69 | Widget build(BuildContext context) { |
70 | return RawAutocomplete<User>( |
71 | optionsBuilder: (TextEditingValue textEditingValue) { |
72 | return _userOptions.where((User option) { |
73 | // Search based on User.toString, which includes both name and |
74 | // email, even though the display string is just the name. |
75 | return option.toString().contains(textEditingValue.text.toLowerCase()); |
76 | }); |
77 | }, |
78 | displayStringForOption: _displayStringForOption, |
79 | fieldViewBuilder: ( |
80 | BuildContext context, |
81 | TextEditingController textEditingController, |
82 | FocusNode focusNode, |
83 | VoidCallback onFieldSubmitted, |
84 | ) { |
85 | return TextFormField( |
86 | controller: textEditingController, |
87 | focusNode: focusNode, |
88 | onFieldSubmitted: (String value) { |
89 | onFieldSubmitted(); |
90 | }, |
91 | ); |
92 | }, |
93 | optionsViewBuilder: (BuildContext context, AutocompleteOnSelected<User> onSelected, Iterable<User> options) { |
94 | return Align( |
95 | alignment: Alignment.topLeft, |
96 | child: Material( |
97 | elevation: 4.0, |
98 | child: SizedBox( |
99 | height: 200.0, |
100 | child: ListView.builder( |
101 | padding: const EdgeInsets.all(8.0), |
102 | itemCount: options.length, |
103 | itemBuilder: (BuildContext context, int index) { |
104 | final User option = options.elementAt(index); |
105 | return GestureDetector( |
106 | onTap: () { |
107 | onSelected(option); |
108 | }, |
109 | child: ListTile( |
110 | title: Text(_displayStringForOption(option)), |
111 | ), |
112 | ); |
113 | }, |
114 | ), |
115 | ), |
116 | ), |
117 | ); |
118 | }, |
119 | ); |
120 | } |
121 | } |
122 | |