Flutter文本搜索字段插件new_textfield_search的使用
Flutter 文本搜索字段插件 new_textfield_search 的使用
简介
new_textfield_search
是一个简单且强大的 Flutter 插件,它支持带有搜索功能的下拉选择。这个插件可以用于离线项目列表或通过过滤 URL 进行在线自定义。
功能特点
- 同步和异步项目(在线、离线、数据库等)
- 可搜索的下拉菜单
- 三种下拉模式:菜单、底部弹出框、模态底部弹出框/对话框
- 单选与多选
- 材料设计风格的下拉菜单
- 易于定制的用户界面
- 支持光亮和暗黑主题
- 轻松集成到无状态小部件中
- 支持多级项目
安装
在 pubspec.yaml
文件中添加依赖:
dependencies:
new_textfield_search: <latest_version>
导入
import 'package:new_textfield_search/new_textfield_search.dart';
简单实现
DropdownSearch<String>(
popupProps: PopupProps.menu(
showSelectedItems: true,
disabledItemFn: (String s) => s.startsWith('I'),
),
items: ["Brazil", "Italia (Disabled)", "Tunisia", 'Canada'],
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: "Menu mode",
hintText: "country in menu mode",
),
),
onChanged: print,
selectedItem: "Brazil",
)
DropdownSearch<String>.multiSelection(
items: ["Brazil", "Italia (Disabled)", "Tunisia", 'Canada'],
popupProps: PopupPropsMultiSelection.menu(
showSelectedItems: true,
disabledItemFn: (String s) => s.startsWith('I'),
),
onChanged: print,
selectedItems: ["Brazil"],
)
自定义显示字段(itemAsString)
DropdownSearch<UserModel>(
asyncItems: (String filter) => getData(filter),
itemAsString: (UserModel u) => u.userAsStringByName(),
onChanged: (UserModel? data) => print(data),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(labelText: "User by name"),
),
)
DropdownSearch<UserModel>(
asyncItems: (String filter) => getData(filter),
itemAsString: (UserModel u) => u.userAsStringById(),
onChanged: (UserModel? data) => print(data),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(labelText: "User by id"),
),
)
自定义筛选函数
DropdownSearch<UserModel>(
filterFn: (user, filter) =>
user.userFilterByCreationDate(filter),
asyncItems: (String filter) => getData(filter),
itemAsString: (UserModel u) => u.userAsStringByName(),
onChanged: (UserModel? data) => print(data),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(labelText: "Name"),
),
)
自定义搜索模式
DropdownSearch<UserModel>(
popupProps: PopupProps.bottomSheet(),
dropdownSearchDecoration: InputDecoration(labelText: "Name"),
asyncItems: (String filter) => getData(filter),
itemAsString: (UserModel u) => u.userAsString(),
onChanged: (UserModel? data) => print(data),
)
验证
DropdownSearch(
items: ["Brazil", "France", "Tunisia", "Canada"],
dropdownSearchDecoration: InputDecoration(labelText: "Name"),
onChanged: print,
selectedItem: "Tunisia",
validator: (String? item) {
if (item == null)
return "Required field";
else if (item == "Brazil")
return "Invalid item";
else
return null;
},
)
示例代码
import 'package:dio/dio.dart';
import 'package:new_textfield_search/new_textfield_search.dart';
import 'package:flutter/material.dart';
import 'user_model.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
title: 'dropdownSearch Demo',
//enable this line if you want test Dark Mode
//theme: ThemeData.dark(),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
[@override](/user/override)
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _formKey = GlobalKey<FormState>();
final _openDropDownProgKey = GlobalKey<DropdownSearchState<int>>();
final _multiKey = GlobalKey<DropdownSearchState<String>>();
final _popupBuilderKey = GlobalKey<DropdownSearchState<String>>();
final _popupCustomValidationKey = GlobalKey<DropdownSearchState<int>>();
final _userEditTextController = TextEditingController(text: 'Mrs');
final myKey = GlobalKey<DropdownSearchState<MultiLevelString>>();
final List<MultiLevelString> myItems = [
MultiLevelString(level1: "1"),
MultiLevelString(level1: "2"),
MultiLevelString(
level1: "3",
subLevel: [
MultiLevelString(level1: "sub3-1"),
MultiLevelString(level1: "sub3-2"),
],
),
MultiLevelString(level1: "4")
];
bool? _popupBuilderSelection = false;
[@override](/user/override)
Widget build(BuildContext context) {
void _handleCheckBoxState({bool updateState = true}) {
var selectedItem = _popupBuilderKey.currentState?.popupGetSelectedItems ?? [];
var isAllSelected = _popupBuilderKey.currentState?.popupIsAllItemSelected ?? false;
_popupBuilderSelection = selectedItem.isEmpty ? false : (isAllSelected ? true : null);
if (updateState) setState(() {});
}
_handleCheckBoxState(updateState: false);
return Scaffold(
appBar: AppBar(title: Text("DropdownSearch Demo")),
body: Padding(
padding: const EdgeInsets.all(25),
child: Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: ListView(
padding: EdgeInsets.all(4),
children: <Widget>[
///************************[simple examples for single and multi selection]************///
Text("[simple examples for single and multi selection]"),
Divider(),
Row(
children: [
Expanded(
child: DropdownSearch<int>(
items: [1, 2, 3, 4, 5, 6, 7],
),
),
Padding(padding: EdgeInsets.all(4)),
Expanded(
child: DropdownSearch<int>.multiSelection(
clearButtonProps: ClearButtonProps(isVisible: true),
items: [1, 2, 3, 4, 5, 6, 7],
),
)
],
),
///************************[simple examples for each mode]*************************///
Padding(padding: EdgeInsets.all(8)),
Text("[simple examples for each mode]"),
Divider(),
Row(
children: [
Expanded(
child: DropdownSearch<int>(
items: [1, 2, 3, 4, 5, 6, 7],
),
),
Padding(padding: EdgeInsets.all(4)),
Expanded(
child: DropdownSearch<int>.multiSelection(
key: _popupCustomValidationKey,
items: [1, 2, 3, 4, 5, 6, 7],
popupProps: PopupPropsMultiSelection.dialog(
validationWidgetBuilder: (ctx, selectedItems) {
return Container(
color: Colors.blue[200],
height: 56,
child: Align(
alignment: Alignment.center,
child: MaterialButton(
child: Text('OK'),
onPressed: () {
_popupCustomValidationKey.currentState?.popupOnValidate();
},
),
),
);
},
),
),
)
],
),
Padding(padding: EdgeInsets.all(4)),
Row(
children: [
Expanded(
child: DropdownSearch<int>(
items: [1, 2, 3, 4, 5, 6, 7],
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: "BottomSheet mode",
hintText: "Select an Int",
),
),
popupProps: PopupProps.bottomSheet(
bottomSheetProps: BottomSheetProps(elevation: 16, backgroundColor: Color(0xFFAADCEE))),
),
),
Padding(padding: EdgeInsets.all(4)),
Expanded(
child: DropdownSearch<int>(
items: [1, 2, 3, 4, 5, 6, 7],
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: "Modal mode",
hintText: "Select an Int",
filled: true,
),
),
popupProps: PopupPropsMultiSelection.modalBottomSheet(
disabledItemFn: (int i) => i <= 3,
),
),
)
],
),
///************************[Favorites examples]**********************************///
Padding(padding: EdgeInsets.all(8)),
Text("[Favorites examples]"),
Divider(),
Row(
children: [
Expanded(
child: DropdownSearch<UserModel>(
asyncItems: (filter) => getData(filter),
compareFn: (i, s) => i.isEqual(s),
popupProps: PopupPropsMultiSelection.modalBottomSheet(
isFilterOnline: true,
showSelectedItems: true,
showSearchBox: true,
itemBuilder: _customPopupItemBuilderExample2,
favoriteItemProps: FavoriteItemProps(
showFavoriteItems: true,
favoriteItems: (us) {
return us.where((e) => e.name.contains("Mrs")).toList();
},
),
),
),
),
Padding(padding: EdgeInsets.all(4)),
Expanded(
child: DropdownSearch<UserModel>.multiSelection(
asyncItems: (filter) => getData(filter),
compareFn: (i, s) => i.isEqual(s),
popupProps: PopupPropsMultiSelection.modalBottomSheet(
showSearchBox: true,
itemBuilder: _customPopupItemBuilderExample2,
favoriteItemProps: FavoriteItemProps(
showFavoriteItems: true,
favoriteItems: (us) {
return us.where((e) => e.name.contains("Mrs")).toList();
},
favoriteItemBuilder: (context, item, isSelected) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(10),
color: Colors.grey[100]),
child: Row(
children: [
Text(
"${item.name}",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.indigo),
),
Padding(padding: EdgeInsets.only(left: 8)),
isSelected ? Icon(Icons.check_box_outlined) : SizedBox.shrink(),
],
),
);
},
),
),
),
),
],
),
///************************[validation examples]********************************///
Padding(padding: EdgeInsets.all(8)),
Text("[validation examples]"),
Divider(),
Row(
children: [
Expanded(
child: DropdownSearch<int>(
items: [1, 2, 3, 4, 5, 6, 7],
autoValidateMode: AutovalidateMode.onUserInteraction,
validator: (int? i) {
if (i == null)
return 'required filed';
else if (i >= 5) return 'value should be < 5';
return null;
},
clearButtonProps: ClearButtonProps(isVisible: true),
),
),
Padding(padding: EdgeInsets.all(4)),
Expanded(
child: DropdownSearch<int>.multiSelection(
items: [1, 2, 3, 4, 5, 6, 7],
validator: (List<int>? items) {
if (items == null || items.isEmpty)
return 'required filed';
else if (items.length > 3) return 'only 1 to 3 items are allowed';
return null;
},
),
)
],
),
///************************[custom popup background examples]********************************///
Padding(padding: EdgeInsets.all(8)),
Text("[custom popup background examples]"),
Divider(),
DropdownSearch<String>(
items: List.generate(5, (index) => "$index"),
popupProps: PopupProps.menu(
fit: FlexFit.loose,
menuProps: MenuProps(
backgroundColor: Colors.transparent,
elevation: 0,
),
containerBuilder: (ctx, popupWidget) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Padding(
padding: const EdgeInsets.only(right: 12),
child: Image.asset(
'assets/images/arrow-up.png',
color: Color(0xFF2F772A),
height: 12,
),
),
Flexible(
child: Container(
child: popupWidget,
color: Color(0xFF2F772A),
),
),
],
);
},
),
),
Padding(padding: EdgeInsets.all(8)),
Row(
children: [
Expanded(
child: DropdownSearch<String>.multiSelection(
key: _popupBuilderKey,
items: List.generate(30, (index) => "$index"),
popupProps: PopupPropsMultiSelection.dialog(
onItemAdded: (l, s) => _handleCheckBoxState(),
onItemRemoved: (l, s) => _handleCheckBoxState(),
showSearchBox: true,
containerBuilder: (ctx, popupWidget) {
return _CheckBoxWidget(
child: popupWidget,
isSelected: _popupBuilderSelection,
onChanged: (v) {
if (v == true)
_popupBuilderKey.currentState!.popupSelectAllItems();
else if (v == false) _popupBuilderKey.currentState!.popupDeselectAllItems();
_handleCheckBoxState();
},
);
},
),
),
),
Padding(padding: EdgeInsets.all(4)),
Expanded(
child: DropdownSearch<String>.multiSelection(
key: _multiKey,
items: List.generate(30, (index) => "$index"),
popupProps: PopupPropsMultiSelection.dialog(
onItemAdded: (l, s) => _handleCheckBoxState(),
onItemRemoved: (l, s) => _handleCheckBoxState(),
showSearchBox: true,
containerBuilder: (ctx, popupWidget) {
return Column(
children: [
Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Padding(
padding: EdgeInsets.all(8),
child: OutlinedButton(
onPressed: () {
// How should I unselect all items in the list?
_multiKey.currentState?.closeDropDownSearch();
},
child: const Text('Cancel'),
),
),
Padding(
padding: EdgeInsets.all(8),
child: OutlinedButton(
onPressed: () {
// How should I select all items in the list?
_multiKey.currentState?.popupSelectAllItems();
},
child: const Text('All'),
),
),
Padding(
padding: EdgeInsets.all(8),
child: OutlinedButton(
onPressed: () {
// How should I unselect all items in the list?
_multiKey.currentState?.popupDeselectAllItems();
},
child: const Text('None'),
),
),
],
),
Expanded(child: popupWidget),
],
);
},
),
),
),
],
),
///************************[dropdownBuilder examples]********************************///
Padding(padding: EdgeInsets.all(8)),
Text("[DropDownSearch builder examples]"),
Divider(),
Row(
children: [
Expanded(
child: DropdownSearch<UserModel>.multiSelection(
asyncItems: (String? filter) => getData(filter),
clearButtonProps: ClearButtonProps(isVisible: true),
popupProps: PopupPropsMultiSelection.modalBottomSheet(
showSelectedItems: true,
isFilterOnline: true,
itemBuilder: _customPopupItemBuilderExample2,
showSearchBox: true,
searchFieldProps: TextFieldProps(
controller: _userEditTextController,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(Icons.clear),
onPressed: () {
_userEditTextController.clear();
},
),
),
),
),
compareFn: (item, selectedItem) => item.id == selectedItem.id,
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: 'Users *',
filled: true,
fillColor: Theme.of(context).inputDecorationTheme.fillColor,
),
),
dropdownBuilder: _customDropDownExampleMultiSelection,
),
),
Padding(padding: EdgeInsets.all(4)),
Expanded(
child: DropdownSearch<UserModel>(
asyncItems: (String? filter) => getData(filter),
popupProps: PopupPropsMultiSelection.modalBottomSheet(
showSelectedItems: true,
itemBuilder: _customPopupItemBuilderExample2,
showSearchBox: true,
),
compareFn: (item, sItem) => item.id == sItem.id,
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
labelText: 'User *',
filled: true,
fillColor: Theme.of(context).inputDecorationTheme.fillColor,
),
),
),
),
],
),
///************************[Dynamic height depending on items number]********************************///
Padding(padding: EdgeInsets.all(8)),
Text("[popup dynamic height examples]"),
Divider(),
Row(
children: [
Expanded(
child: DropdownSearch<int>(
items: List.generate(50, (i) => i),
popupProps: PopupProps.menu(
showSearchBox: true,
title: Text('default fit'),
),
),
),
Padding(padding: EdgeInsets.all(4)),
Expanded(
child: DropdownSearch<int>(
items: List.generate(50, (i) => i),
popupProps: PopupProps.menu(
title: Text('With fit to loose and no constraints'),
showSearchBox: true,
fit: FlexFit.loose,
//comment this if you want that the items do not takes all available height
constraints: BoxConstraints.tightFor(),
),
),
)
],
),
Padding(padding: EdgeInsets.all(4)),
Row(
children: [
Expanded(
child: DropdownSearch<int>(
items: List.generate(50, (i) => i),
popupProps: PopupProps.menu(
showSearchBox: true,
fit: FlexFit.loose,
title: Text('fit to a specific max height'),
constraints: BoxConstraints(maxHeight: 300),
),
),
),
Padding(padding: EdgeInsets.all(4)),
Expanded(
child: DropdownSearch<int>(
items: List.generate(50, (i) => i),
popupProps: PopupProps.menu(
title: Text('fit to a specific width and height'),
showSearchBox: true,
fit: FlexFit.loose,
constraints: BoxConstraints.tightFor(
width: 300,
height: 300,
),
),
),
)
],
),
///************************[Handle dropdown programmatically]********************************///
Padding(padding: EdgeInsets.all(8)),
Text("[handle dropdown programmatically]"),
Divider(),
DropdownSearch<int>(
key: _openDropDownProgKey,
items: [1, 2, 3],
),
Padding(padding: EdgeInsets.all(4)),
ElevatedButton(
onPressed: () {
_openDropDownProgKey.currentState?.changeSelectedItem(100);
},
child: Text('set to 100'),
),
Padding(padding: EdgeInsets.all(4)),
ElevatedButton(
onPressed: () {
_openDropDownProgKey.currentState?.openDropDownSearch();
},
child: Text('open popup'),
),
///************************[multiLevel items example]********************************///
Padding(padding: EdgeInsets.all(8)),
Text("[multiLevel items example]"),
Divider(),
DropdownSearch<MultiLevelString>(
key: myKey,
items: myItems,
compareFn: (i1, i2) => i1.level1 == i2.level1,
popupProps: PopupProps.menu(
showSelectedItems: true,
interceptCallBacks: true, //important line
itemBuilder: (ctx, item, isSelected) {
return ListTile(
selected: isSelected,
title: Text(item.level1),
trailing: item.subLevel.isEmpty
? null
: (item.isExpanded
? IconButton(
icon: Icon(Icons.arrow_drop_down),
onPressed: () {
item.isExpanded = !item.isExpanded;
myKey.currentState?.updatePopupState();
},
)
: IconButton(
icon: Icon(Icons.arrow_right),
onPressed: () {
item.isExpanded = !item.isExpanded;
myKey.currentState?.updatePopupState();
},
)),
subtitle: item.subLevel.isNotEmpty && item.isExpanded
? Container(
height: item.subLevel.length * 50,
child: ListView(
children: item.subLevel
.map(
(e) => ListTile(
selected: myKey.currentState?.getSelectedItem?.level1 == e.level1,
title: Text(e.level1),
onTap: () {
myKey.currentState?.popupValidate([e]);
},
),
)
.toList(),
),
)
: null,
onTap: () => myKey.currentState?.popupValidate([item]),
);
},
),
),
],
),
),
),
);
}
Widget _customDropDownExampleMultiSelection(BuildContext context, List<UserModel> selectedItems) {
if (selectedItems.isEmpty) {
return ListTile(
contentPadding: EdgeInsets.all(0),
leading: CircleAvatar(),
title: Text("No item selected"),
);
}
return Wrap(
children: selectedItems.map((e) {
return Padding(
padding: const EdgeInsets.all(4.0),
child: Container(
child: ListTile(
contentPadding: EdgeInsets.all(0),
leading: CircleAvatar(
backgroundImage: NetworkImage(e.avatar),
),
title: Text(e.name),
subtitle: Text(
e.createdAt.toString(),
),
),
),
);
}).toList(),
);
}
Widget _customPopupItemBuilderExample2(BuildContext context, UserModel item, bool isSelected) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 8),
decoration: !isSelected
? null
: BoxDecoration(
border: Border.all(color: Theme.of(context).primaryColor),
borderRadius: BorderRadius.circular(5),
color: Colors.white,
),
child: ListTile(
selected: isSelected,
title: Text(item.name),
subtitle: Text(item.createdAt.toString()),
leading: CircleAvatar(
backgroundImage: NetworkImage(item.avatar),
),
),
);
}
Future<List<UserModel>> getData(filter) async {
var response = await Dio().get(
"https://63c1210999c0a15d28e1ec1d.mockapi.io/users",
queryParameters: {"filter": filter},
);
final data = response.data;
if (data != null) {
return UserModel.fromJsonList(data);
}
return [];
}
}
class _CheckBoxWidget extends StatefulWidget {
final Widget child;
final bool? isSelected;
final ValueChanged<bool?>? onChanged;
_CheckBoxWidget({required this.child, this.isSelected, this.onChanged});
[@override](/user/override)
CheckBoxState createState() => CheckBoxState();
}
class CheckBoxState extends State<_CheckBoxWidget> {
bool? isSelected;
[@override](/user/override)
void initState() {
super.initState();
isSelected = widget.isSelected;
}
[@override](/user/override)
void didUpdateWidget(covariant _CheckBoxWidget oldWidget) {
if (widget.isSelected != isSelected) isSelected = widget.isSelected;
super.didUpdateWidget(oldWidget);
}
[@override](/user/override)
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Color(0x88F44336),
Colors.blue,
],
),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Text('Select: '),
Checkbox(
value: isSelected,
tristate: true,
onChanged: (bool? v) {
if (v == null) v = false;
setState(() {
isSelected = v;
if (widget.onChanged != null) widget.onChanged!(v);
});
}),
],
),
Expanded(child: widget.child),
],
),
);
}
}
class MultiLevelString {
final String level1;
final List<MultiLevelString> subLevel;
bool isExpanded;
MultiLevelString({
this.level1 = "",
this.subLevel = const [],
this.isExpanded = false,
});
MultiLevelString copy({
String? level1,
List<MultiLevelString>? subLevel,
bool? isExpanded,
}) =>
MultiLevelString(
level1: level1 ?? this.level1,
subLevel: subLevel ?? this.subLevel,
isExpanded: isExpanded ?? this.isExpanded,
);
[@override](/user/override)
String toString() => level1;
}
更多关于Flutter文本搜索字段插件new_textfield_search的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter文本搜索字段插件new_textfield_search的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何使用 new_textfield_search
插件的 Flutter 代码示例。new_textfield_search
是一个用于创建文本搜索字段的 Flutter 插件,它提供了丰富的功能和自定义选项。
首先,确保你已经在 pubspec.yaml
文件中添加了该插件的依赖项:
dependencies:
flutter:
sdk: flutter
new_textfield_search: ^1.0.0 # 请检查最新版本号
然后,在你的 Dart 文件中使用 NewTextFieldSearch
组件。下面是一个完整的示例:
import 'package:flutter/material.dart';
import 'package:new_textfield_search/new_textfield_search.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final List<String> items = [
'Apple',
'Banana',
'Cherry',
'Date',
'Elderberry',
'Fig',
'Grape',
'Honeydew',
];
String? searchResult;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Text Search Field Demo'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
NewTextFieldSearch(
controller: TextEditingController(),
hintText: 'Search...',
onSearchTextChanged: (text) {
setState(() {
// 简单的搜索逻辑,这里可以根据需要自定义
searchResult = items
.where((item) => item.toLowerCase().contains(text.toLowerCase()))
.toList()
.join(", ");
});
},
decoration: InputDecoration(
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.search),
),
),
SizedBox(height: 16.0),
Text(
searchResult ?? 'No search results',
style: TextStyle(fontSize: 18.0),
),
],
),
),
);
}
@override
void dispose() {
// 清理 TextEditingController,防止内存泄漏
super.dispose();
}
}
代码解释:
-
依赖导入:在
pubspec.yaml
文件中添加new_textfield_search
插件的依赖项。 -
主应用结构:
MyApp
类是 Flutter 应用的入口点,它使用MaterialApp
创建一个应用。 -
主页面:
MyHomePage
是一个有状态的组件,它包含搜索字段和显示搜索结果的部分。 -
搜索字段:
NewTextFieldSearch
组件用于创建搜索字段。controller
:使用TextEditingController
来管理文本输入。hintText
:显示提示文本,例如 “Search…”。onSearchTextChanged
:当搜索文本改变时的回调函数。这里我们简单地过滤了items
列表,并显示匹配结果。decoration
:自定义搜索字段的外观,包括边框和前缀图标。
-
搜索结果:
Text
组件用于显示搜索结果。如果searchResult
为空,则显示 “No search results”。 -
内存管理:在
dispose
方法中清理TextEditingController
,防止内存泄漏。
这个示例展示了如何使用 new_textfield_search
插件来创建一个基本的文本搜索字段,并根据用户的输入动态显示搜索结果。你可以根据需要进一步自定义和扩展这个示例。