首先,大家先看一下效果图:
看起来就是一行行的显示,但是小伙伴千万不要觉得使用Row就能解决,还涉及到换行呢,你总不至于要一个个算长度来换行吧?所以这里推荐使用Wrap,来看下Wrap部分怎么使用:
Wrap(
spacing: 12.0, // 主轴(水平)方向间距
runSpacing: 0, // 纵轴(垂直)方向间距
alignment: WrapAlignment.start,
children: _keywords
.map<Widget>((e) => new GestureDetector(
child: Chip(
backgroundColor: Color(0xFFF5F5F5),
label: new Text(
e,
style: TextStyle(color: Color(0xFF7C7070), fontSize: 13),
),
),
onTap: () {
setState(() {
this._keyword = e;
textEditingController.text = e;
});
goToSearch(this._keyword);
},
))
.toList(),
),
使用Wrap的好处就是可以满一行后自动换行,且最大的item不会超过一行,这里item显示的样式可以根据具体的UI来给,也可以自定义Container来做。博主这里使用的是官方提供的组件Chip,默认就是椭圆形的,但是缺点是高度不可调,宽度会随着文字变化而变化,看看怎么用吧:
Chip(
backgroundColor: Color(0xFFF5F5F5),
label: new Text(
e,
style: TextStyle(color: Color(0xFF7C7070), fontSize: 13),
),
),
在Chip里面有这些属性:
Key key,
this.avatar,
@required this.label,
this.labelStyle,
this.labelPadding,
this.deleteIcon,
this.onDeleted,
this.deleteIconColor,
this.deleteButtonTooltipMessage,
this.shape,
this.clipBehavior = Clip.none,
this.focusNode,
this.autofocus = false,
this.backgroundColor,
this.padding,
this.materialTapTargetSize,
this.elevation,
this.shadowColor,
可以设置左右的icon,下面给个案例:
Chip(
backgroundColor: Color(0xFFF7F7F7),
avatar: Icon(
xxxxx,
size: 14,
color: Color(0xFFC9CCCF),
),
label: '搜索商品',style: TextStyle(fontSize: 12, color: Color(0xFF202020),)),
padding: EdgeInsets.all(5),
///设置右侧icon和文字之间的间距
labelPadding: EdgeInsets.only(right: 50),
deleteIcon: Icon(
xxxx,
color: Color(0xFFC7C7C7),
size: 14,
),
deleteIconColor: Colors.red,
onDeleted: () {
},
),
这样一来,是不是基本上核心部分就解决了?
完整的实现逻辑代码放在下面,比较长,有需要的可以参考:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:sp_util/sp_util.dart';
class SearchHistory extends StatefulWidget {
const SearchHistory({Key key}) : super(key: key);
@override
_SearchHistoryState createState() => _SearchHistoryState();
}
class _SearchHistoryState extends State<SearchHistory>with AutomaticKeepAliveClientMixin<SearchHistory>{
var type;
String _keyword = '';
bool _hasFocus = true;
List<String> _keywords = new List();
final FocusNode _focusNode = FocusNode();
/// 搜索输入框hint内容
String get _hint => '搜索店铺或商品';
final TextEditingController textEditingController =
new TextEditingController();
@override
void initState() {
// TODO: implement initState
this._keywords..addAll(_getSearchKeywords());
_focusNode.addListener(() {
setState(() {
_hasFocus = _focusNode.hasFocus;
});
});
super.initState();
}
void _serachData(String text) async {
goToSearch(text);
}
@override
void dispose() {
_focusNode?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
var columns = Column(
children: <Widget>[
///搜索历史头部标题和删除按钮
searchHisHeaderView(),
///搜索历史item
searchHisItemView(),
///剩余空间
_searchBottomView(),
],
);
return Scaffold(
backgroundColor: Colors.white,
appBar: PreferredSize(
preferredSize: Size.fromHeight(44),
child: SafeArea(child: searchBoxView()),
),
body: Container(
color: Color.fromRGBO(249, 249, 250, 1),
child: columns),
);
}
/// 构建搜索顶部栏
Widget searchBoxView() {
return new Column(
children: <Widget>[
PreferredSize(
child: new Container(
height: 43.5,
padding: new EdgeInsets.all(0),
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
InkWell(
child: Container(
child: IconButton(
icon: Icon(Icons.arrow_back, color:Colors.black),
onPressed: () => Navigator.of(context).pop(),
)
),
onTap: () {
Navigator.pop(context);
},
),
new Expanded(
child: ConstrainedBox(
constraints: BoxConstraints(
maxHeight: 38,
),
/// 搜索关键字输入栏
child: new TextField(
autofocus: true,
focusNode: _focusNode,
textAlignVertical: TextAlignVertical.center,
style: new TextStyle(fontSize: 15.0, color: Colors.black),
decoration: new InputDecoration(
contentPadding: EdgeInsets.fromLTRB(10, 0, 10, 0),
filled: true,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10), //边角为20
),
borderSide: BorderSide(
color: Color(0xFFE2E4EB),
width: 0.5, //边线宽度为1
),
),
fillColor: Color(0xFFFEFEFE),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(10), //边角为20
),
borderSide: BorderSide(
color: Color(0xFFE2E4EB),
width: 0.5, //边线宽度为1
),
),
hintText: _hint,
hintStyle: TextStyle(color: Colors.grey),
alignLabelWithHint: true,
prefixIcon: new Icon(
Icons.search,
size: 17,
color: Color(0xFF8e8e93),
),
suffixIcon: Offstage(
offstage: _keyword.length == 0,
child: GestureDetector(
child: Container(
width: 38,
height: 38,
alignment: Alignment.center,
color: Colors.transparent,
child: new Icon(
Icons.delete,
size: 18,
color: Color(0xFF8e8e93),
),
),
onTap: () {
setState(() {
_keyword = '';
textEditingController.text = '';
});
})),
),
onChanged: (String content) {
print(content);
_keyword = content;
setState(() {
});
},
onSubmitted: (String content) {
goToSearch(content ?? "");
},
onTap: () {
setState(() {
});
},
controller: textEditingController,
)),
),
InkWell(
borderRadius: BorderRadius.all(Radius.circular(4)),
autofocus: false,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 18, vertical: 0),
child: Text('搜索',
style:
TextStyle(fontSize: 17, color: Color(0xFF3479FD)))),
onTap: () {
goToSearch(_keyword ?? "");
},
),
],
),
),
),
],
);
}
Widget searchHisHeaderView() {
print('隐藏头');
if ( _keyword.length > 0) {
return Offstage();
}
var length = _keywords?.length ?? 0;
if (length == 0) {
return Offstage();
}
return new Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
new Container(
child: Text(
'搜索历史',
style: TextStyle(
fontSize: 17,
color: Color(0xFF202020),
fontWeight: FontWeight.bold),
),
height: 36,
margin: EdgeInsets.only(left: 16, top: 10),
),
InkWell(
child: new Container(
alignment: Alignment.center,
margin: EdgeInsets.all(10),
child: Icon(
Icons.delete,
size: 13,
),
width: 30.0,
height: 30.0,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(15.0),
color: Color(0xFFF7F7F7))),
onTap: () {
refreshHisLists(null);
},
),
]);
}
Widget searchHisItemView() {
if ( _keyword.length > 0) {
return Offstage();
}
var length = _keywords?.length ?? 0;
if (length == 0) {
return Offstage();
}
return Container(
width: double.infinity,
padding: EdgeInsets.only(left: 16, right: 16),
child: Wrap(
spacing: 12.0, // 主轴(水平)方向间距
runSpacing: 0, // 纵轴(垂直)方向间距
alignment: WrapAlignment.start,
children: _keywords
.map<Widget>((e) => new GestureDetector(
child: Chip(
backgroundColor: Color(0xFFF5F5F5),
label: new Text(
e,
style: TextStyle(color: Color(0xFF7C7070), fontSize: 13),
),
),
onTap: () {
setState(() {
this._keyword = e;
textEditingController.text = e;
});
goToSearch(this._keyword);
},
))
.toList(),
),
);
}
/// 构建页面剩余空间
Widget _searchBottomView() {
if (_keyword.length > 0) {
return Offstage();
}
var length = _keywords?.length ?? 0;
if (length == 0) {
return Offstage();
}
return Expanded(
child: Container(
margin: EdgeInsets.only(top: 10),
color: Colors.grey[100],
),
);
}
///点击搜索
Future<void> goToSearch(String content) async {
if (content.length == 0) return;
content = content?.trim() ?? '';
///在不失去焦点的情况下隐藏键盘
SystemChannels.textInput.invokeMethod('TextInput.hide');
if (content.isNotEmpty) {
///非空才记录数据
var hisList = _keywords ?? [];
///删除已经存在的记录
hisList.remove(content);
///新记录放前面
hisList.insert(0, content);
///保留最多15条记录
if (hisList.length > 15) {
hisList.removeLast();
}
///刷新记录
refreshHisLists(hisList);
}
}
///设置搜索关键字
void refreshHisLists(List<String> keywords) {
String key = 'search_history';
if (keywords == null) {
SpUtil.putString(key, null);
} else {
SpUtil.putString(key, json.encode(keywords));
}
setState(() {
_keywords = keywords ?? [];
});
}
///查询搜索关键字
List<String> _getSearchKeywords() {
String key = 'search_history';
String value = SpUtil.getString(key, defValue: null);
if (value == null) {
return [];
}
return (json.decode(value) as List<dynamic>).cast<String>();
}
@override
// TODO: implement wantKeepAlive
bool get wantKeepAlive => true;
}
SpUtil是一个第三方提供的库,需要自己引入哦:
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
sp_util: ^1.0.1
以上,你学会了么?快来自己试试吧。