Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
f3947ea0
Unverified
Commit
f3947ea0
authored
Jan 23, 2021
by
Justin McCandless
Committed by
GitHub
Jan 23, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Autocomplete (Material) (#73753)
* Material Autocomplete widget
parent
d9a69e3b
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
542 additions
and
8 deletions
+542
-8
material.dart
packages/flutter/lib/material.dart
+1
-0
autocomplete.dart
packages/flutter/lib/src/material/autocomplete.dart
+253
-0
autocomplete.dart
packages/flutter/lib/src/widgets/autocomplete.dart
+32
-8
autocomplete_test.dart
packages/flutter/test/material/autocomplete_test.dart
+256
-0
No files found.
packages/flutter/lib/material.dart
View file @
f3947ea0
...
@@ -22,6 +22,7 @@ export 'src/material/app.dart';
...
@@ -22,6 +22,7 @@ export 'src/material/app.dart';
export
'src/material/app_bar.dart'
;
export
'src/material/app_bar.dart'
;
export
'src/material/app_bar_theme.dart'
;
export
'src/material/app_bar_theme.dart'
;
export
'src/material/arc.dart'
;
export
'src/material/arc.dart'
;
export
'src/material/autocomplete.dart'
;
export
'src/material/back_button.dart'
;
export
'src/material/back_button.dart'
;
export
'src/material/banner.dart'
;
export
'src/material/banner.dart'
;
export
'src/material/banner_theme.dart'
;
export
'src/material/banner_theme.dart'
;
...
...
packages/flutter/lib/src/material/autocomplete.dart
0 → 100644
View file @
f3947ea0
// 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<String> _kOptions = <String>[
/// 'aardvark',
/// 'bobcat',
/// 'chameleon',
/// ];
///
/// @override
/// Widget build(BuildContext context) {
/// return Autocomplete<String>(
/// optionsBuilder: (TextEditingValue textEditingValue) {
/// if (textEditingValue.text == '') {
/// return const Iterable<String>.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<User> _userOptions = <User>[
/// 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<User>(
/// displayStringForOption: _displayStringForOption,
/// optionsBuilder: (TextEditingValue textEditingValue) {
/// if (textEditingValue.text == '') {
/// return const Iterable<User>.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
<
T
extends
Object
>
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
<
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
;
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
,
optionsBuilder:
optionsBuilder
,
optionsViewBuilder:
optionsViewBuilder
??
(
BuildContext
context
,
AutocompleteOnSelected
<
T
>
onSelected
,
Iterable
<
T
>
options
)
{
return
_AutocompleteOptions
<
T
>(
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
<
T
extends
Object
>
extends
StatelessWidget
{
const
_AutocompleteOptions
({
Key
?
key
,
required
this
.
displayStringForOption
,
required
this
.
onSelected
,
required
this
.
options
,
})
:
super
(
key:
key
);
final
AutocompleteOptionToString
<
T
>
displayStringForOption
;
final
AutocompleteOnSelected
<
T
>
onSelected
;
final
Iterable
<
T
>
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:
const
EdgeInsets
.
all
(
0.0
),
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
)),
),
);
},
),
),
),
);
}
}
packages/flutter/lib/src/widgets/autocomplete.dart
View file @
f3947ea0
...
@@ -16,6 +16,7 @@ import 'overlay.dart';
...
@@ -16,6 +16,7 @@ import 'overlay.dart';
/// entered so far.
/// entered so far.
///
///
/// See also:
/// See also:
///
/// * [RawAutocomplete.optionsBuilder], which is of this type.
/// * [RawAutocomplete.optionsBuilder], which is of this type.
typedef
AutocompleteOptionsBuilder
<
T
extends
Object
>
=
Iterable
<
T
>
Function
(
TextEditingValue
textEditingValue
);
typedef
AutocompleteOptionsBuilder
<
T
extends
Object
>
=
Iterable
<
T
>
Function
(
TextEditingValue
textEditingValue
);
...
@@ -23,6 +24,7 @@ typedef AutocompleteOptionsBuilder<T extends Object> = Iterable<T> Function(Text
...
@@ -23,6 +24,7 @@ typedef AutocompleteOptionsBuilder<T extends Object> = Iterable<T> Function(Text
/// that the user has selected an option.
/// that the user has selected an option.
///
///
/// See also:
/// See also:
///
/// * [RawAutocomplete.onSelected], which is of this type.
/// * [RawAutocomplete.onSelected], which is of this type.
typedef
AutocompleteOnSelected
<
T
extends
Object
>
=
void
Function
(
T
option
);
typedef
AutocompleteOnSelected
<
T
extends
Object
>
=
void
Function
(
T
option
);
...
@@ -31,6 +33,7 @@ typedef AutocompleteOnSelected<T extends Object> = void Function(T option);
...
@@ -31,6 +33,7 @@ typedef AutocompleteOnSelected<T extends Object> = void Function(T option);
/// selects an option.
/// selects an option.
///
///
/// See also:
/// See also:
///
/// * [RawAutocomplete.optionsViewBuilder], which is of this type.
/// * [RawAutocomplete.optionsViewBuilder], which is of this type.
typedef
AutocompleteOptionsViewBuilder
<
T
extends
Object
>
=
Widget
Function
(
typedef
AutocompleteOptionsViewBuilder
<
T
extends
Object
>
=
Widget
Function
(
BuildContext
context
,
BuildContext
context
,
...
@@ -42,6 +45,7 @@ typedef AutocompleteOptionsViewBuilder<T extends Object> = Widget Function(
...
@@ -42,6 +45,7 @@ typedef AutocompleteOptionsViewBuilder<T extends Object> = Widget Function(
/// contains the input [TextField] or [TextFormField].
/// contains the input [TextField] or [TextFormField].
///
///
/// See also:
/// See also:
///
/// * [RawAutocomplete.fieldViewBuilder], which is of this type.
/// * [RawAutocomplete.fieldViewBuilder], which is of this type.
typedef
AutocompleteFieldViewBuilder
=
Widget
Function
(
typedef
AutocompleteFieldViewBuilder
=
Widget
Function
(
BuildContext
context
,
BuildContext
context
,
...
@@ -54,19 +58,21 @@ typedef AutocompleteFieldViewBuilder = Widget Function(
...
@@ -54,19 +58,21 @@ typedef AutocompleteFieldViewBuilder = Widget Function(
/// a string which can be displayed in the widget's options menu.
/// a string which can be displayed in the widget's options menu.
///
///
/// See also:
/// See also:
///
/// * [RawAutocomplete.displayStringForOption], which is of this type.
/// * [RawAutocomplete.displayStringForOption], which is of this type.
typedef
AutocompleteOptionToString
<
T
extends
Object
>
=
String
Function
(
T
option
);
typedef
AutocompleteOptionToString
<
T
extends
Object
>
=
String
Function
(
T
option
);
// TODO(justinmc): Mention Autocomplete
and AutocompleteCupertino when they are
// TODO(justinmc): Mention Autocomplete
Cupertino when it is implemented.
//
implemented.
//
/ {@template flutter.widgets.RawAutocomplete.RawAutocomplete}
/// A widget for helping the user make a selection by entering some text and
/// A widget for helping the user make a selection by entering some text and
/// choosing from among a list of options.
/// choosing from among a list of options.
///
///
/// This is a core framework widget with very basic UI.
///
/// The user's text input is received in a field built with the
/// The user's text input is received in a field built with the
/// [fieldViewBuilder] parameter. The options to be displayed are determined
/// [fieldViewBuilder] parameter. The options to be displayed are determined
/// using [optionsBuilder] and rendered with [optionsViewBuilder].
/// using [optionsBuilder] and rendered with [optionsViewBuilder].
/// {@endtemplate}
///
/// This is a core framework widget with very basic UI.
///
///
/// {@tool dartpad --template=freeform}
/// {@tool dartpad --template=freeform}
/// This example shows how to create a very basic autocomplete widget using the
/// This example shows how to create a very basic autocomplete widget using the
...
@@ -419,6 +425,11 @@ typedef AutocompleteOptionToString<T extends Object> = String Function(T option)
...
@@ -419,6 +425,11 @@ typedef AutocompleteOptionToString<T extends Object> = String Function(T option)
/// }
/// }
/// ```
/// ```
/// {@end-tool}
/// {@end-tool}
///
/// See also:
///
/// * [Autocomplete], which is a Material-styled implementation that is based
/// on RawAutocomplete.
class
RawAutocomplete
<
T
extends
Object
>
extends
StatefulWidget
{
class
RawAutocomplete
<
T
extends
Object
>
extends
StatefulWidget
{
/// Create an instance of RawAutocomplete.
/// Create an instance of RawAutocomplete.
///
///
...
@@ -428,7 +439,7 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
...
@@ -428,7 +439,7 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
Key
?
key
,
Key
?
key
,
required
this
.
optionsViewBuilder
,
required
this
.
optionsViewBuilder
,
required
this
.
optionsBuilder
,
required
this
.
optionsBuilder
,
this
.
displayStringForOption
=
_
defaultStringForOption
,
this
.
displayStringForOption
=
defaultStringForOption
,
this
.
fieldViewBuilder
,
this
.
fieldViewBuilder
,
this
.
focusNode
,
this
.
focusNode
,
this
.
onSelected
,
this
.
onSelected
,
...
@@ -444,10 +455,12 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
...
@@ -444,10 +455,12 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
assert
((
focusNode
==
null
)
==
(
textEditingController
==
null
)),
assert
((
focusNode
==
null
)
==
(
textEditingController
==
null
)),
super
(
key:
key
);
super
(
key:
key
);
/// {@template flutter.widgets.RawAutocomplete.fieldViewBuilder}
/// Builds the field whose input is used to get the options.
/// Builds the field whose input is used to get the options.
///
///
/// Pass the provided [TextEditingController] to the field built here so that
/// Pass the provided [TextEditingController] to the field built here so that
/// RawAutocomplete can listen for changes.
/// RawAutocomplete can listen for changes.
/// {@endtemplate}
final
AutocompleteFieldViewBuilder
?
fieldViewBuilder
;
final
AutocompleteFieldViewBuilder
?
fieldViewBuilder
;
/// The [FocusNode] that is used for the text field.
/// The [FocusNode] that is used for the text field.
...
@@ -552,30 +565,38 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
...
@@ -552,30 +565,38 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
/// not null.
/// not null.
final
FocusNode
?
focusNode
;
final
FocusNode
?
focusNode
;
/// {@template flutter.widgets.RawAutocomplete.optionsViewBuilder}
/// Builds the selectable options widgets from a list of options objects.
/// Builds the selectable options widgets from a list of options objects.
///
///
/// The options are displayed floating below the field using a
/// The options are displayed floating below the field using a
/// [CompositedTransformFollower] inside of an [Overlay], not at the same
/// [CompositedTransformFollower] inside of an [Overlay], not at the same
/// place in the widget tree as RawAutocomplete.
/// place in the widget tree as [RawAutocomplete].
/// {@endtemplate}
final
AutocompleteOptionsViewBuilder
<
T
>
optionsViewBuilder
;
final
AutocompleteOptionsViewBuilder
<
T
>
optionsViewBuilder
;
/// {@template flutter.widgets.RawAutocomplete.displayStringForOption}
/// Returns the string to display in the field when the option is selected.
/// Returns the string to display in the field when the option is selected.
///
///
/// This is useful when using a custom T type and the string to display is
/// This is useful when using a custom T type and the string to display is
/// different than the string to search by.
/// different than the string to search by.
///
///
/// If not provided, will use `option.toString()`.
/// If not provided, will use `option.toString()`.
/// {@endtemplate}
final
AutocompleteOptionToString
<
T
>
displayStringForOption
;
final
AutocompleteOptionToString
<
T
>
displayStringForOption
;
/// {@template flutter.widgets.RawAutocomplete.onSelected}
/// Called when an option is selected by the user.
/// Called when an option is selected by the user.
///
///
/// Any [TextEditingController] listeners will not be called when the user
/// Any [TextEditingController] listeners will not be called when the user
/// selects an option, even though the field will update with the selected
/// selects an option, even though the field will update with the selected
/// value, so use this to be informed of selection.
/// value, so use this to be informed of selection.
/// {@endtemplate}
final
AutocompleteOnSelected
<
T
>?
onSelected
;
final
AutocompleteOnSelected
<
T
>?
onSelected
;
/// {@template flutter.widgets.RawAutocomplete.optionsBuilder}
/// A function that returns the current selectable options objects given the
/// A function that returns the current selectable options objects given the
/// current TextEditingValue.
/// current TextEditingValue.
/// {@endtemplate}
final
AutocompleteOptionsBuilder
<
T
>
optionsBuilder
;
final
AutocompleteOptionsBuilder
<
T
>
optionsBuilder
;
/// The [TextEditingController] that is used for the text field.
/// The [TextEditingController] that is used for the text field.
...
@@ -603,8 +624,11 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
...
@@ -603,8 +624,11 @@ class RawAutocomplete<T extends Object> extends StatefulWidget {
rawAutocomplete
.
_onFieldSubmitted
();
rawAutocomplete
.
_onFieldSubmitted
();
}
}
// The default way to convert an option to a string.
/// The default way to convert an option to a string in
static
String
_defaultStringForOption
(
dynamic
option
)
{
/// [displayStringForOption].
///
/// Simply uses the `toString` method on the option.
static
String
defaultStringForOption
(
dynamic
option
)
{
return
option
.
toString
();
return
option
.
toString
();
}
}
...
...
packages/flutter/test/material/autocomplete_test.dart
0 → 100644
View file @
f3947ea0
// 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/material.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
class
User
{
const
User
({
required
this
.
email
,
required
this
.
name
,
});
final
String
email
;
final
String
name
;
@override
String
toString
()
{
return
'
$name
,
$email
'
;
}
}
void
main
(
)
{
const
List
<
String
>
kOptions
=
<
String
>[
'aardvark'
,
'bobcat'
,
'chameleon'
,
'dingo'
,
'elephant'
,
'flamingo'
,
'goose'
,
'hippopotamus'
,
'iguana'
,
'jaguar'
,
'koala'
,
'lemur'
,
'mouse'
,
'northern white rhinocerous'
,
];
const
List
<
User
>
kOptionsUsers
=
<
User
>[
User
(
name:
'Alice'
,
email:
'alice@example.com'
),
User
(
name:
'Bob'
,
email:
'bob@example.com'
),
User
(
name:
'Charlie'
,
email:
'charlie123@gmail.com'
),
];
testWidgets
(
'can filter and select a list of string options'
,
(
WidgetTester
tester
)
async
{
late
String
lastSelection
;
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Autocomplete
<
String
>(
onSelected:
(
String
selection
)
{
lastSelection
=
selection
;
},
optionsBuilder:
(
TextEditingValue
textEditingValue
)
{
return
kOptions
.
where
((
String
option
)
{
return
option
.
contains
(
textEditingValue
.
text
.
toLowerCase
());
});
},
),
),
),
);
// The field is always rendered, but the options are not unless needed.
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
expect
(
find
.
byType
(
ListView
),
findsNothing
);
// Focus the empty field. All the options are displayed.
await
tester
.
tap
(
find
.
byType
(
TextFormField
));
await
tester
.
pump
();
expect
(
find
.
byType
(
ListView
),
findsOneWidget
);
ListView
list
=
find
.
byType
(
ListView
).
evaluate
().
first
.
widget
as
ListView
;
expect
(
list
.
semanticChildCount
,
kOptions
.
length
);
// Enter text. The options are filtered by the text.
await
tester
.
enterText
(
find
.
byType
(
TextFormField
),
'ele'
);
await
tester
.
pump
();
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
expect
(
find
.
byType
(
ListView
),
findsOneWidget
);
list
=
find
.
byType
(
ListView
).
evaluate
().
first
.
widget
as
ListView
;
// 'chameleon' and 'elephant' are displayed.
expect
(
list
.
semanticChildCount
,
2
);
// Select a option. The options hide and the field updates to show the
// selection.
await
tester
.
tap
(
find
.
byType
(
InkWell
).
first
);
await
tester
.
pump
();
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
expect
(
find
.
byType
(
ListView
),
findsNothing
);
final
TextFormField
field
=
find
.
byType
(
TextFormField
).
evaluate
().
first
.
widget
as
TextFormField
;
expect
(
field
.
controller
!.
text
,
'chameleon'
);
expect
(
lastSelection
,
'chameleon'
);
// Modify the field text. The options appear again and are filtered.
await
tester
.
enterText
(
find
.
byType
(
TextFormField
),
'e'
);
await
tester
.
pump
();
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
expect
(
find
.
byType
(
ListView
),
findsOneWidget
);
list
=
find
.
byType
(
ListView
).
evaluate
().
first
.
widget
as
ListView
;
// 'chameleon', 'elephant', 'goose', 'lemur', 'mouse', and
// 'northern white rhinocerous' are displayed.
expect
(
list
.
semanticChildCount
,
6
);
});
testWidgets
(
'can filter and select a list of custom User options'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Autocomplete
<
User
>(
optionsBuilder:
(
TextEditingValue
textEditingValue
)
{
return
kOptionsUsers
.
where
((
User
option
)
{
return
option
.
toString
().
contains
(
textEditingValue
.
text
.
toLowerCase
());
});
},
),
),
),
);
// The field is always rendered, but the options are not unless needed.
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
expect
(
find
.
byType
(
ListView
),
findsNothing
);
// Focus the empty field. All the options are displayed.
await
tester
.
tap
(
find
.
byType
(
TextFormField
));
await
tester
.
pump
();
expect
(
find
.
byType
(
ListView
),
findsOneWidget
);
ListView
list
=
find
.
byType
(
ListView
).
evaluate
().
first
.
widget
as
ListView
;
expect
(
list
.
semanticChildCount
,
kOptionsUsers
.
length
);
// Enter text. The options are filtered by the text.
await
tester
.
enterText
(
find
.
byType
(
TextFormField
),
'example'
);
await
tester
.
pump
();
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
expect
(
find
.
byType
(
ListView
),
findsOneWidget
);
list
=
find
.
byType
(
ListView
).
evaluate
().
first
.
widget
as
ListView
;
// 'Alice' and 'Bob' are displayed because they have "example.com" emails.
expect
(
list
.
semanticChildCount
,
2
);
// Select a option. The options hide and the field updates to show the
// selection.
await
tester
.
tap
(
find
.
byType
(
InkWell
).
first
);
await
tester
.
pump
();
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
expect
(
find
.
byType
(
ListView
),
findsNothing
);
final
TextFormField
field
=
find
.
byType
(
TextFormField
).
evaluate
().
first
.
widget
as
TextFormField
;
expect
(
field
.
controller
!.
text
,
'Alice, alice@example.com'
);
// Modify the field text. The options appear again and are filtered.
await
tester
.
enterText
(
find
.
byType
(
TextFormField
),
'B'
);
await
tester
.
pump
();
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
expect
(
find
.
byType
(
ListView
),
findsOneWidget
);
list
=
find
.
byType
(
ListView
).
evaluate
().
first
.
widget
as
ListView
;
// 'Bob' is displayed.
expect
(
list
.
semanticChildCount
,
1
);
});
testWidgets
(
'displayStringForOption is displayed in the options'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Autocomplete
<
User
>(
displayStringForOption:
(
User
option
)
{
return
option
.
name
;
},
optionsBuilder:
(
TextEditingValue
textEditingValue
)
{
return
kOptionsUsers
.
where
((
User
option
)
{
return
option
.
toString
().
contains
(
textEditingValue
.
text
.
toLowerCase
());
});
},
),
),
),
);
// The field is always rendered, but the options are not unless needed.
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
expect
(
find
.
byType
(
ListView
),
findsNothing
);
// Focus the empty field. All the options are displayed, and the string that
// is used comes from displayStringForOption.
await
tester
.
tap
(
find
.
byType
(
TextFormField
));
await
tester
.
pump
();
expect
(
find
.
byType
(
ListView
),
findsOneWidget
);
final
ListView
list
=
find
.
byType
(
ListView
).
evaluate
().
first
.
widget
as
ListView
;
expect
(
list
.
semanticChildCount
,
kOptionsUsers
.
length
);
for
(
int
i
=
0
;
i
<
kOptionsUsers
.
length
;
i
++)
{
expect
(
find
.
text
(
kOptionsUsers
[
i
].
name
),
findsOneWidget
);
}
// Select a option. The options hide and the field updates to show the
// selection. The text in the field is given by displayStringForOption.
await
tester
.
tap
(
find
.
byType
(
InkWell
).
first
);
await
tester
.
pump
();
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
expect
(
find
.
byType
(
ListView
),
findsNothing
);
final
TextFormField
field
=
find
.
byType
(
TextFormField
).
evaluate
().
first
.
widget
as
TextFormField
;
expect
(
field
.
controller
!.
text
,
kOptionsUsers
.
first
.
name
);
});
testWidgets
(
'can build a custom field'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
fieldKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Autocomplete
<
String
>(
optionsBuilder:
(
TextEditingValue
textEditingValue
)
{
return
kOptions
.
where
((
String
option
)
{
return
option
.
contains
(
textEditingValue
.
text
.
toLowerCase
());
});
},
fieldViewBuilder:
(
BuildContext
context
,
TextEditingController
textEditingController
,
FocusNode
focusNode
,
VoidCallback
onFieldSubmitted
)
{
return
Container
(
key:
fieldKey
);
},
),
),
),
);
// The custom field is rendered and not the default TextFormField.
expect
(
find
.
byKey
(
fieldKey
),
findsOneWidget
);
expect
(
find
.
byType
(
TextFormField
),
findsNothing
);
});
testWidgets
(
'can build custom options'
,
(
WidgetTester
tester
)
async
{
final
GlobalKey
optionsKey
=
GlobalKey
();
await
tester
.
pumpWidget
(
MaterialApp
(
home:
Scaffold
(
body:
Autocomplete
<
String
>(
optionsBuilder:
(
TextEditingValue
textEditingValue
)
{
return
kOptions
.
where
((
String
option
)
{
return
option
.
contains
(
textEditingValue
.
text
.
toLowerCase
());
});
},
optionsViewBuilder:
(
BuildContext
context
,
AutocompleteOnSelected
<
String
>
onSelected
,
Iterable
<
String
>
options
)
{
return
Container
(
key:
optionsKey
);
},
),
),
),
);
// The default field is rendered but not the options, yet.
expect
(
find
.
byKey
(
optionsKey
),
findsNothing
);
expect
(
find
.
byType
(
TextFormField
),
findsOneWidget
);
// Focus the empty field. The custom options is displayed.
await
tester
.
tap
(
find
.
byType
(
TextFormField
));
await
tester
.
pump
();
expect
(
find
.
byKey
(
optionsKey
),
findsOneWidget
);
});
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment