如何在 flutter 中创建工具栏搜索视图
how to create toolbar searchview in flutter
我需要在我的应用程序的工具栏中实现searchview,以实现列表视图列表过滤器。如下图,我搜索了很多仍然没有得到正确的答案。任何帮助将不胜感激,提前致谢。
您只需在用户点击图标时切换状态。除了在您这边进行一些重构和清理代码之外,这个简单的示例应该可以帮助您。
class SearchAppBar extends StatefulWidget {
@override
_SearchAppBarState createState() => new _SearchAppBarState();
}
class _SearchAppBarState extends State<SearchAppBar> {
Widget appBarTitle = new Text("AppBar Title");
Icon actionIcon = new Icon(Icons.search);
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
centerTitle: true,
title:appBarTitle,
actions: <Widget>[
new IconButton(icon: actionIcon,onPressed:(){
setState(() {
if ( this.actionIcon.icon == Icons.search){
this.actionIcon = new Icon(Icons.close);
this.appBarTitle = new TextField(
style: new TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
prefixIcon: new Icon(Icons.search,color: Colors.white),
hintText: "Search...",
hintStyle: new TextStyle(color: Colors.white)
),
);}
else {
this.actionIcon = new Icon(Icons.search);
this.appBarTitle = new Text("AppBar Title");
}
});
} ,),]
),
);
}
}
在@aziza 回答的帮助下,我在下面编写了带有列表过滤器的搜索视图的详细代码片段。对其他人有帮助
import 'package:flutter/material.dart';
class SearchList extends StatefulWidget {
SearchList({ Key key }) : super(key: key);
@override
_SearchListState createState() => new _SearchListState();
}
class _SearchListState extends State<SearchList>
{
Widget appBarTitle = new Text("Search Sample", 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<String> _list;
bool _IsSearching;
String _searchText = "";
_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;
init();
}
void init() {
_list = List();
_list.add("Google");
_list.add("IOS");
_list.add("Andorid");
_list.add("Dart");
_list.add("Flutter");
_list.add("Python");
_list.add("React");
_list.add("Xamarin");
_list.add("Kotlin");
_list.add("Java");
_list.add("RxAndroid");
}
@override
Widget build(BuildContext context) {
return new Scaffold(
key: key,
appBar: buildBar(context),
body: new ListView(
padding: new EdgeInsets.symmetric(vertical: 8.0),
children: _IsSearching ? _buildSearchList() : _buildList(),
),
);
}
List<ChildItem> _buildList() {
return _list.map((contact) => new ChildItem(contact)).toList();
}
List<ChildItem> _buildSearchList() {
if (_searchText.isEmpty) {
return _list.map((contact) => new ChildItem(contact))
.toList();
}
else {
List<String> _searchList = List();
for (int i = 0; i < _list.length; i++) {
String name = _list.elementAt(i);
if (name.toLowerCase().contains(_searchText.toLowerCase())) {
_searchList.add(name);
}
}
return _searchList.map((contact) => new ChildItem(contact))
.toList();
}
}
Widget buildBar(BuildContext context) {
return new AppBar(
centerTitle: true,
title: appBarTitle,
actions: <Widget>[
new IconButton(icon: actionIcon, onPressed: () {
setState(() {
if (this.actionIcon.icon == Icons.search) {
this.actionIcon = new Icon(Icons.close, color: Colors.white,);
this.appBarTitle = new TextField(
controller: _searchQuery,
style: new TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
prefixIcon: new Icon(Icons.search, color: Colors.white),
hintText: "Search...",
hintStyle: 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("Search Sample", style: new TextStyle(color: Colors.white),);
_IsSearching = false;
_searchQuery.clear();
});
}
}
class ChildItem extends StatelessWidget {
final String name;
ChildItem(this.name);
@override
Widget build(BuildContext context) {
return new ListTile(title: new Text(this.name));
}
}
输出:
您可以通过编辑AppBar 的leading、title 和actions 来实现。如下图所示。
appBar: new AppBar(
leading: _isSearching ? const BackButton() : null,
title: _isSearching ? _buildSearchField() : _buildTitle(context),
actions: _buildActions(),
),
你可以here详细看看。他们已经为此构建了一个简单的演示。
如果您想要一个简单的搜索栏,可以使用自定义的 TextField
import 'package:flutter/material.dart';
class SearchBar extends StatelessWidget {
final void Function(String) onTextChange;
SearchBar({ this.onTextChange });
@override
Widget build(BuildContext context) {
return Container(
height: 50,
padding: EdgeInsets.all(8),
child: TextField(
onChanged: onTextChange,
decoration: InputDecoration(
fillColor: Colors.black.withOpacity(0.1),
filled: true,
prefixIcon: Icon(Icons.search),
hintText: 'Search something ...',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide.none),
contentPadding: EdgeInsets.zero
)
)
);
}
}
屏幕截图(空安全):
您应该使用 Flutter 开箱即用的 SearchDelegate
。这是一个如何工作的小视频:
完整代码:
class SearchPage extends StatefulWidget {
@override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
String? _result;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Search')),
body: Center(
child: Column(
children: <Widget>[
Text(_result ?? '', style: TextStyle(fontSize: 18)),
ElevatedButton(
onPressed: () async {
var result = await showSearch<String>(
context: context,
delegate: CustomDelegate(),
);
setState(() => _result = result);
},
child: Text('Search'),
),
],
),
),
);
}
}
class CustomDelegate extends SearchDelegate<String> {
List<String> data = nouns.take(100).toList();
@override
List<Widget> buildActions(BuildContext context) => [IconButton(icon: Icon(Icons.clear), onPressed: () => query = '')];
@override
Widget buildLeading(BuildContext context) => IconButton(icon: Icon(Icons.chevron_left), onPressed: () => close(context, ''));
@override
Widget buildResults(BuildContext context) => Container();
@override
Widget buildSuggestions(BuildContext context) {
var listToShow;
if (query.isNotEmpty)
listToShow = data.where((e) => e.contains(query) && e.startsWith(query)).toList();
else
listToShow = data;
return ListView.builder(
itemCount: listToShow.length,
itemBuilder: (_, i) {
var noun = listToShow[i];
return ListTile(
title: Text(noun),
onTap: () => close(context, noun),
);
},
);
}
}
还有 Flutter flutter_search_view_pk 库,您可以使用它来实现 Youtube/Instagram 使用的相同类型的设计。
如何集成使用?
1 : 直接将pk_search_bar
目录拖放到你的Flutter项目中。
2 : 创建一个 Class SearchScreen
并在 build
> Scaffold
> child: SearchBar()
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchScreen()),
);
例子
Widget searchBar(BuildContext context) {
return SearchBar<CountryModel>(
searchBarPadding: EdgeInsets.only(left: 5, right: 5, top: 10, bottom: 5),
headerPadding: EdgeInsets.only(left: 0, right: 0),
listPadding: EdgeInsets.only(left: 0, right: 0),
hintText: "Search Placeholder",
hintStyle: TextStyle(
color: Colors.black54,
),
textStyle: TextStyle(
color: Colors.black,
fontWeight: FontWeight.normal,
),
iconActiveColor: Colors.deepPurple,
shrinkWrap: true,
mainAxisSpacing: 5,
crossAxisSpacing: 5,
suggestions: widget.countryModelList,
cancellationWidget: Text("Cancel"),
minimumChars: 1,
emptyWidget: Center(
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Text("There is no any data found"),
),
),
onError: (error) {
return Center(
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Text("$error", textAlign: TextAlign.center),
),
);
},
loader: Center(
child: LoadingIndicator(),
),
onSearch: getCountrySearchWithSuggestion, /// CountrySearch // if
want to search with API then use thi ----> getCountryListFromApi
onCancelled: () {
Navigator.pop(context);
},
buildSuggestion: (CountryModel countryModel, int index) {
return countryGenerateColumn(countryModel, index);
},
onItemFound: (CountryModel countryModel, int index) {
return countryGenerateColumn(countryModel, index);
},
);
}
我需要在我的应用程序的工具栏中实现searchview,以实现列表视图列表过滤器。如下图,我搜索了很多仍然没有得到正确的答案。任何帮助将不胜感激,提前致谢。
您只需在用户点击图标时切换状态。除了在您这边进行一些重构和清理代码之外,这个简单的示例应该可以帮助您。
class SearchAppBar extends StatefulWidget {
@override
_SearchAppBarState createState() => new _SearchAppBarState();
}
class _SearchAppBarState extends State<SearchAppBar> {
Widget appBarTitle = new Text("AppBar Title");
Icon actionIcon = new Icon(Icons.search);
@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
centerTitle: true,
title:appBarTitle,
actions: <Widget>[
new IconButton(icon: actionIcon,onPressed:(){
setState(() {
if ( this.actionIcon.icon == Icons.search){
this.actionIcon = new Icon(Icons.close);
this.appBarTitle = new TextField(
style: new TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
prefixIcon: new Icon(Icons.search,color: Colors.white),
hintText: "Search...",
hintStyle: new TextStyle(color: Colors.white)
),
);}
else {
this.actionIcon = new Icon(Icons.search);
this.appBarTitle = new Text("AppBar Title");
}
});
} ,),]
),
);
}
}
在@aziza 回答的帮助下,我在下面编写了带有列表过滤器的搜索视图的详细代码片段。对其他人有帮助
import 'package:flutter/material.dart';
class SearchList extends StatefulWidget {
SearchList({ Key key }) : super(key: key);
@override
_SearchListState createState() => new _SearchListState();
}
class _SearchListState extends State<SearchList>
{
Widget appBarTitle = new Text("Search Sample", 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<String> _list;
bool _IsSearching;
String _searchText = "";
_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;
init();
}
void init() {
_list = List();
_list.add("Google");
_list.add("IOS");
_list.add("Andorid");
_list.add("Dart");
_list.add("Flutter");
_list.add("Python");
_list.add("React");
_list.add("Xamarin");
_list.add("Kotlin");
_list.add("Java");
_list.add("RxAndroid");
}
@override
Widget build(BuildContext context) {
return new Scaffold(
key: key,
appBar: buildBar(context),
body: new ListView(
padding: new EdgeInsets.symmetric(vertical: 8.0),
children: _IsSearching ? _buildSearchList() : _buildList(),
),
);
}
List<ChildItem> _buildList() {
return _list.map((contact) => new ChildItem(contact)).toList();
}
List<ChildItem> _buildSearchList() {
if (_searchText.isEmpty) {
return _list.map((contact) => new ChildItem(contact))
.toList();
}
else {
List<String> _searchList = List();
for (int i = 0; i < _list.length; i++) {
String name = _list.elementAt(i);
if (name.toLowerCase().contains(_searchText.toLowerCase())) {
_searchList.add(name);
}
}
return _searchList.map((contact) => new ChildItem(contact))
.toList();
}
}
Widget buildBar(BuildContext context) {
return new AppBar(
centerTitle: true,
title: appBarTitle,
actions: <Widget>[
new IconButton(icon: actionIcon, onPressed: () {
setState(() {
if (this.actionIcon.icon == Icons.search) {
this.actionIcon = new Icon(Icons.close, color: Colors.white,);
this.appBarTitle = new TextField(
controller: _searchQuery,
style: new TextStyle(
color: Colors.white,
),
decoration: new InputDecoration(
prefixIcon: new Icon(Icons.search, color: Colors.white),
hintText: "Search...",
hintStyle: 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("Search Sample", style: new TextStyle(color: Colors.white),);
_IsSearching = false;
_searchQuery.clear();
});
}
}
class ChildItem extends StatelessWidget {
final String name;
ChildItem(this.name);
@override
Widget build(BuildContext context) {
return new ListTile(title: new Text(this.name));
}
}
输出:
您可以通过编辑AppBar 的leading、title 和actions 来实现。如下图所示。
appBar: new AppBar(
leading: _isSearching ? const BackButton() : null,
title: _isSearching ? _buildSearchField() : _buildTitle(context),
actions: _buildActions(),
),
你可以here详细看看。他们已经为此构建了一个简单的演示。
如果您想要一个简单的搜索栏,可以使用自定义的 TextField
import 'package:flutter/material.dart';
class SearchBar extends StatelessWidget {
final void Function(String) onTextChange;
SearchBar({ this.onTextChange });
@override
Widget build(BuildContext context) {
return Container(
height: 50,
padding: EdgeInsets.all(8),
child: TextField(
onChanged: onTextChange,
decoration: InputDecoration(
fillColor: Colors.black.withOpacity(0.1),
filled: true,
prefixIcon: Icon(Icons.search),
hintText: 'Search something ...',
border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide.none),
contentPadding: EdgeInsets.zero
)
)
);
}
}
屏幕截图(空安全):
您应该使用 Flutter 开箱即用的 SearchDelegate
。这是一个如何工作的小视频:
完整代码:
class SearchPage extends StatefulWidget {
@override
_SearchPageState createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
String? _result;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Search')),
body: Center(
child: Column(
children: <Widget>[
Text(_result ?? '', style: TextStyle(fontSize: 18)),
ElevatedButton(
onPressed: () async {
var result = await showSearch<String>(
context: context,
delegate: CustomDelegate(),
);
setState(() => _result = result);
},
child: Text('Search'),
),
],
),
),
);
}
}
class CustomDelegate extends SearchDelegate<String> {
List<String> data = nouns.take(100).toList();
@override
List<Widget> buildActions(BuildContext context) => [IconButton(icon: Icon(Icons.clear), onPressed: () => query = '')];
@override
Widget buildLeading(BuildContext context) => IconButton(icon: Icon(Icons.chevron_left), onPressed: () => close(context, ''));
@override
Widget buildResults(BuildContext context) => Container();
@override
Widget buildSuggestions(BuildContext context) {
var listToShow;
if (query.isNotEmpty)
listToShow = data.where((e) => e.contains(query) && e.startsWith(query)).toList();
else
listToShow = data;
return ListView.builder(
itemCount: listToShow.length,
itemBuilder: (_, i) {
var noun = listToShow[i];
return ListTile(
title: Text(noun),
onTap: () => close(context, noun),
);
},
);
}
}
还有 Flutter flutter_search_view_pk 库,您可以使用它来实现 Youtube/Instagram 使用的相同类型的设计。
如何集成使用?
1 : 直接将pk_search_bar
目录拖放到你的Flutter项目中。
2 : 创建一个 Class SearchScreen
并在 build
> Scaffold
> child: SearchBar()
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchScreen()),
);
例子
Widget searchBar(BuildContext context) {
return SearchBar<CountryModel>(
searchBarPadding: EdgeInsets.only(left: 5, right: 5, top: 10, bottom: 5),
headerPadding: EdgeInsets.only(left: 0, right: 0),
listPadding: EdgeInsets.only(left: 0, right: 0),
hintText: "Search Placeholder",
hintStyle: TextStyle(
color: Colors.black54,
),
textStyle: TextStyle(
color: Colors.black,
fontWeight: FontWeight.normal,
),
iconActiveColor: Colors.deepPurple,
shrinkWrap: true,
mainAxisSpacing: 5,
crossAxisSpacing: 5,
suggestions: widget.countryModelList,
cancellationWidget: Text("Cancel"),
minimumChars: 1,
emptyWidget: Center(
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Text("There is no any data found"),
),
),
onError: (error) {
return Center(
child: Padding(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Text("$error", textAlign: TextAlign.center),
),
);
},
loader: Center(
child: LoadingIndicator(),
),
onSearch: getCountrySearchWithSuggestion, /// CountrySearch // if
want to search with API then use thi ----> getCountryListFromApi
onCancelled: () {
Navigator.pop(context);
},
buildSuggestion: (CountryModel countryModel, int index) {
return countryGenerateColumn(countryModel, index);
},
onItemFound: (CountryModel countryModel, int index) {
return countryGenerateColumn(countryModel, index);
},
);
}