[英]Flutter search bar with autocomplete
I'm looking for a search bar in flutter docs but can't find it, is there a widget for the search bar with autocomplete in appbar.我正在flutter docs中寻找搜索栏,但找不到它,是否有搜索栏小部件在appbar中具有自动完成功能。 For example, I have a search icon on my appbar.
例如,我的应用栏上有一个搜索图标。 When one press it show's the search box, when you type it should show autocomplete from the dropdown with listtile.
当按下它时会显示搜索框,当您键入它时,它应该会从带有 listtile 的下拉列表中显示自动完成。 I managed to implement this but it's not easy to use because I need a dropdown to show suggestion autocomplete, then use the suggestion for a new route if selected.
我设法实现了这一点,但它并不容易使用,因为我需要一个下拉列表来显示建议自动完成,然后如果选择了新路线的建议。
You can use Stack to achieve the autocomplete dropdown box effect.您可以使用 Stack 来实现自动完成下拉框效果。 Example below has 2 Containers - both hold
ListView
as child objects.下面的示例有 2 个容器 - 都将
ListView
作为子对象。 One holds search results, other has some random text as content for the body.一个保存搜索结果,另一个包含一些随机文本作为正文的内容。
ListView
(search result) is placed inside an Align Object and alignment property is set to Alignment.topCenter. ListView
(搜索结果)放置在一个 Align Object 内,并且将对齐属性设置为 Alignment.topCenter。 This ensures that List appears at the top, just below the AppBar
.这确保 List 出现在顶部,就在
AppBar
下方。
Updated the Post (accepted answer) mentioned in the comments for a complete a demo.更新了评论中提到的帖子(接受的答案)以获得完整的演示。
As explained above:如上所述:
@override
Widget build(BuildContext context) {
return new Scaffold(
key: key,
appBar: buildBar(context),
body: new Stack(
children: <Widget>[
new Container(
height: 300.0,
padding: EdgeInsets.all(10.0),
child: new DefaultTabController(length: 5, child: mainTabView),
),
displaySearchResults(),
],
));
}
Widget displaySearchResults() {
if (_IsSearching) {
return new Align(
alignment: Alignment.topCenter,
//heightFactor: 0.0,
child: searchList());
} else {
return new Align(alignment: Alignment.topCenter, child: new Container());
}
}
Complete demo完整演示
class SearchList extends StatefulWidget {
SearchList({Key key, this.name}) : super(key: key);
final String name;
@override
_SearchListState createState() => new _SearchListState();
}
class _SearchListState extends State<SearchList> {
Widget appBarTitle = new Text(
"",
style: new TextStyle(color: Colors.white),
);
Icon actionIcon = new Icon(
Icons.search,
color: Colors.white,
);
final key = new GlobalKey<ScaffoldState>();
final TextEditingController _searchQuery = new TextEditingController();
List<SearchResult> _list;
bool _IsSearching;
String _searchText = "";
String selectedSearchValue = "";
_SearchListState() {
_searchQuery.addListener(() {
if (_searchQuery.text.isEmpty) {
setState(() {
_IsSearching = false;
_searchText = "";
});
} else {
setState(() {
_IsSearching = true;
_searchText = _searchQuery.text;
});
}
});
}
@override
void initState() {
super.initState();
_IsSearching = false;
createSearchResultList();
}
void createSearchResultList() {
_list = <SearchResult>[
new SearchResult(name: 'Google'),
new SearchResult(name: 'IOS'),
new SearchResult(name: 'IOS2'),
new SearchResult(name: 'Android'),
new SearchResult(name: 'Dart'),
new SearchResult(name: 'Flutter'),
new SearchResult(name: 'Python'),
new SearchResult(name: 'React'),
new SearchResult(name: 'Xamarin'),
new SearchResult(name: 'Kotlin'),
new SearchResult(name: 'Java'),
new SearchResult(name: 'RxAndroid'),
];
}
@override
Widget build(BuildContext context) {
return new Scaffold(
key: key,
appBar: buildBar(context),
body: new Stack(
children: <Widget>[
new Container(
height: 300.0,
padding: EdgeInsets.all(10.0),
child: new Container(
child: ListView(
children: <Widget>[
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
new Text("Hello World!"),
],
),
),
),
displaySearchResults(),
],
));
}
Widget displaySearchResults() {
if (_IsSearching) {
return new Align(
alignment: Alignment.topCenter,
child: searchList());
} else {
return new Align(alignment: Alignment.topCenter, child: new Container());
}
}
ListView searchList() {
List<SearchResult> results = _buildSearchList();
return ListView.builder(
itemCount: _buildSearchList().isEmpty == null ? 0 : results.length,
itemBuilder: (context, int index) {
return Container(
decoration: new BoxDecoration(
color: Colors.grey[100],
border: new Border(
bottom: new BorderSide(
color: Colors.grey,
width: 0.5
)
)
),
child: ListTile(
onTap: (){},
title: Text(results.elementAt(index).name,
style: new TextStyle(fontSize: 18.0)),
),
);
},
);
}
List<SearchResult> _buildList() {
return _list.map((result) => new SearchResult(name: result.name)).toList();
}
List<SearchResult> _buildSearchList() {
if (_searchText.isEmpty) {
return _list.map((result) => new SearchResult(name: result.name)).toList();
} else {
List<SearchResult> _searchList = List();
for (int i = 0; i < _list.length; i++) {
SearchResult result = _list.elementAt(i);
if ((result.name).toLowerCase().contains(_searchText.toLowerCase())) {
_searchList.add(result);
}
}
return _searchList
.map((result) => new SearchResult(name: result.name))
.toList();
}
}
Widget buildBar(BuildContext context) {
return new AppBar(
centerTitle: true,
title: appBarTitle,
actions: <Widget>[
new IconButton(
icon: actionIcon,
onPressed: () {
_displayTextField();
},
),
// new IconButton(icon: new Icon(Icons.more), onPressed: _IsSearching ? _showDialog(context, _buildSearchList()) : _showDialog(context,_buildList()))
],
);
}
String selectedPopupRoute = "My Home";
final List<String> popupRoutes = <String>[
"My Home",
"Favorite Room 1",
"Favorite Room 2"
];
void _displayTextField() {
setState(() {
if (this.actionIcon.icon == Icons.search) {
this.actionIcon = new Icon(
Icons.close,
color: Colors.white,
);
this.appBarTitle = new TextField(
autofocus: true,
controller: _searchQuery,
style: new TextStyle(
color: Colors.white,
),
);
_handleSearchStart();
} else {
_handleSearchEnd();
}
});
}
void _handleSearchStart() {
setState(() {
_IsSearching = true;
});
}
void _handleSearchEnd() {
setState(() {
this.actionIcon = new Icon(
Icons.search,
color: Colors.white,
);
this.appBarTitle = new Text(
"",
style: new TextStyle(color: Colors.white),
);
_IsSearching = false;
_searchQuery.clear();
});
}
}
this plugin will helpful for you, loader_search_bar这个插件对你有帮助, loader_search_bar
Flutter widget integrating search field feature into app bar, allowing to receive query change callbacks and automatically load new data set into ListView. Flutter 小部件将搜索字段功能集成到应用栏中,允许接收查询更改回调并自动将新数据集加载到 ListView。 It replaces standard AppBar widget and needs to be placed underneath Scaffold element in the widget tree to work properly.
它取代了标准的 AppBar 小部件,需要放置在小部件树中的 Scaffold 元素下方才能正常工作。
Getting started To start using SearchBar insert it in place of an AppBar element in the Scaffold widget.入门 要开始使用 SearchBar,请将其插入 Scaffold 小部件中的 AppBar 元素位置。 Regardless of the use case, defaultBar named argument has to be specified, which basically is a widget that will be displayed whenever SearchBar is not in activated state:
无论用例如何,都必须指定 defaultBar 命名参数,它基本上是一个小部件,只要 SearchBar 未处于激活状态就会显示:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: SearchBar(
defaultBar: AppBar(
leading: IconButton(
icon: Icon(Icons.menu),
onPressed: _openDrawer,
),
title: Text('Default app bar title'),
),
...
),
body: _body,
drawer: _drawer,
);
}
Optional attributes可选属性
Query callbacks查询回调
To get notified about user input specify onQueryChanged and/or onQuerySubmitted callback functions that receive current query string as an argument:要获得有关用户输入的通知,请指定接收当前查询字符串作为参数的 onQueryChanged 和/或 onQuerySubmitted 回调函数:
appBar: SearchBar(
...
onQueryChanged: (query) => _handleQueryChanged(context, query),
onQuerySubmitted: (query) => _handleQuerySubmitted(context, query),
),
QuerySetLoader查询集加载器
By passing QuerySetLoader object as an argument one can additionally benefit from search results being automatically built as ListView widget whenever search query changes:通过将 QuerySetLoader 对象作为参数传递,当搜索查询更改时,您还可以从搜索结果自动构建为 ListView 小部件中受益:
appBar: SearchBar(
...
loader: QuerySetLoader<Item>(
querySetCall: _getItemListForQuery,
itemBuilder: _buildItemWidget,
loadOnEachChange: true,
animateChanges: true,
),
),
List<Item> _getItemListForQuery(String query) { ... }
Widget _buildItemWidget(Item item) { ... }
SearchItem搜索项
Specifying this parameter allows to customize how search item should be built and positioned in app bar.指定此参数允许自定义搜索项应如何在应用栏中构建和定位。 It can be either action or menu widget.
它可以是动作或菜单小部件。 No matter which of these two is picked, two constructor arguments can be passed:
无论选择这两个中的哪一个,都可以传递两个构造函数参数:
SearchItem.action搜索项操作
appBar: SearchBar(
// ...
searchItem: SearchItem.action(
builder: (_) => Padding(
padding: EdgeInsets.all(12.0),
child: Icon(
Icons.find_in_page,
color: Colors.indigoAccent,
),
),
gravity: SearchItemGravity.exactly(1),
),
)
SearchItem.menu搜索项目菜单
appBar: SearchBar(
// ...
searchItem: SearchItem.menu(
builder: (_) => PopupMenuItem(
child: Text("Search 🔍"),
value: "search",
),
gravity: SearchItemGravity.end,
),
)
Also, bear in mind that SearchBar will prevent built item widget from receiving tap events and will begin search action rather than that.另外,请记住,SearchBar 将阻止构建的项目小部件接收点击事件,并将开始搜索操作而不是那样。
hope it will help you.希望它会帮助你。
It is actually very simple.其实很简单。 you can refer above answers for details.
你可以参考上面的答案了解详情。 Let's follow these steps:
让我们按照以下步骤操作:
void initState() {
filteredList.addAll(autoList);
}
Create a custom search bar widget with a TextField in it创建一个带有 TextField 的自定义搜索栏小部件
we will be getting a 'value' ie the text entered from this Textfield: eg.我们将获得一个“值”,即从此文本字段输入的文本:例如。 TextFiled(onchange(value){})
TextFiled(onchange(value){})
Assuming that we have strings in our autoList, write:假设我们的 autoList 中有字符串,写:
filteredList.removeWhere((i) => i.contains(value.toString())==false);
The complete TextField widget will look like:完整的 TextField 小部件将如下所示:
TextField(
onChanged: (value) {
setState(() {
filteredList.clear(); //for the next time that we search we want the list to be unfilterted
filteredList.addAll(autoList); //getting list to original state
//removing items that do not contain the entered Text
filteredList.removeWhere((i) => i.contains(value.toString())==false);
//following is just a bool parameter to keep track of lists
searched=!searched;
});
},
controller: editingController,
decoration: InputDecoration(
border: InputBorder.none,
labelText: "Search for the filtered list",
prefixIcon: Icon(Icons.search),
),
),
Now, along the search bar, we just have to display filteredList with ListViewBuilder.现在,沿着搜索栏,我们只需要使用 ListViewBuilder 显示过滤列表。 done :)
完毕 :)
import 'package:flutter/material.dart';
class SearchText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Searchable Text"),
actions: <Widget>[
IconButton(
icon: Icon(Icons.search),
onPressed: () {
showSearch(
context: context,
delegate: DataSearch(),
);
})
],
),
drawer: Drawer(),
);
}
}
class DataSearch extends SearchDelegate<String> {
final cities = ['Ankara', 'İzmir', 'İstanbul', 'Samsun', 'Sakarya'];
var recentCities = ['Ankara'];
@override
List<Widget> buildActions(BuildContext context) {
return [
IconButton(
icon: Icon(Icons.clear),
onPressed: () {
query = "";
})
];
}
@override
Widget buildLeading(BuildContext context) {
return IconButton(
icon: AnimatedIcon(
icon: AnimatedIcons.menu_arrow,
progress: transitionAnimation,
),
onPressed: () {
close(context, null);
});
}
@override
Widget buildResults(BuildContext context) {
return Center(
child: Container(
width: 100,
height: 100,
child: Card(
color: Colors.red,
child: Center(child: Text(query)),
),
),
);
}
@override
Widget buildSuggestions(BuildContext context) {
final suggestionList = query.isEmpty
? recentCities
: cities.where((p) => p.startsWith(query)).toList();
return ListView.builder(
itemBuilder: (context, index) => ListTile(
onTap: () {
showResults(context);
},
leading: Icon(Icons.location_city),
title: RichText(
text: TextSpan(
text: suggestionList[index].substring(0, query.length),
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
children: [
TextSpan(
text: suggestionList[index].substring(query.length),
),
],
),
),
),
itemCount: suggestionList.length,
);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.