Flutter 常用控件
函数:
//获取map 的 key 对应 bool类型的value
bool MapValuebool(dynamic map, dynamic key,{bool defaultBool = false}){
if(!(map is Map)){
return defaultBool;
}
if(StrIsEmpty(key)){
return defaultBool;
}
Map myMap = map as Map;
if(myMap.keys.length<1){
return defaultBool;
}
if(myMap.containsKey(key) == false){
return defaultBool;
}
var res = myMap[key];
if(res is bool){
return res;
}
if(res == 1){
return true;
}
if(res == 0){
return false;
}
return defaultBool;
}
///判断为空
bool StrIsEmpty(dynamic object) {
if (object == null) return true;
if (object == []) return true;
if (object == '') return true;
if (object == 'null') return true;
if (object == '(null)') return true;
if (object == '<null>') return true;
if (object is String && object.isEmpty) {
return true;
} else if (object is List && object.isEmpty) {
return true;
} else if (object is Map && object.isEmpty) {
return true;
}
return false;
}
///判断为非空
bool StrIsNotEmpty(dynamic object) {
bool isEmpty = StrIsEmpty(object);
return isEmpty==false;
}
分割线:
class XLSeperatorLine extends StatelessWidget {
//在括号{}外
//在括号{}内
double h;
Color color;
double margLeft;
double margRight;
XLSeperatorLine({
this.h = 0.5,
this.color=ColorUtils.lineColor,
this.margLeft = 0,
this.margRight = 0,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
return Padding(padding: EdgeInsets.only(top: 0,bottom: 0, left: margLeft, right: margRight),
child: Container(
color: color,
height: h,
),
);
}
}
空白分割View:
class XLSeperatorView extends StatelessWidget {
//在括号{}外
//在括号{}内
double h;
Color color;
double margLeft;
double margRight;
XLSeperatorView({
this.h = 10,
this.color=ColorUtils.cF7F7F7,
this.margLeft = 0,
this.margRight = 0,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
return XLSeperatorLine(
h: h,
color: color,
margLeft: margLeft,
margRight: margRight,
);
}
}
圆角阴影背景View(适用于通用下拉滚动ListView的cell):
class XLShadowCornerBGView extends StatelessWidget {
Widget child;//子控件
Color bgColor;//背景颜色
EdgeInsetsGeometry outMarg;//外边距(自己边框距离外部父控件位置)
EdgeInsetsGeometry inPadd;//内边距(内容距离自己边框的位置)
Function ?clickFun;//点击事件
bool isIgnoreSubClick;//阻挡子控件的点击事件
XLShadowCornerBGView({
required this.child,
this.bgColor = Colors.white,
this.outMarg = const EdgeInsets.only(top:15,left: 15,right: 15,bottom: 7.5),
this.inPadd = const EdgeInsets.only(top:15,left: 15,right: 15,bottom: 15),
this.isIgnoreSubClick = false,//阻挡子控件的事件
this.clickFun,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
//IgnorePointer忽视事件传递
Widget SuperWidget({required Widget subW}){
if(isIgnoreSubClick) {
return IgnorePointer(
child: subW,
);
} else {
return subW;
}
}
return Container(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: (){
if(clickFun!=null){
clickFun!();
}
},
child: SuperWidget(
subW: Container(
margin: outMarg,//外边距
padding: inPadd,//内编辑
decoration: BoxDecoration(
color: bgColor, // 底色
boxShadow: const <BoxShadow>[
BoxShadow(
blurRadius: 4, //阴影范围
spreadRadius: 1, //阴影浓度
color: ColorUtils.cShadowColor, //阴影颜色
),
],
borderRadius: BorderRadius.circular(5), // 圆角也可控件一边圆角大小
//borderRadius: BorderRadius.only(
// topRight: Radius.circular(10),
// bottomRight: Radius.circular(10)) //单独加某一边圆角
),
child:child,
),
)
),
);
}
}
加载网络图片:
用了一个extended_image
import 'package:extended_image/extended_image.dart';
import 'package:flutter/material.dart';
实现:
class XLNetImageView extends StatelessWidget {
String netUrl;
double imgWidth;
double imgHeight;
double angleRadius;
String defaultImgName;
BlendMode ?colorBlendMode;
bool isPortrait;//是人的头像
XLNetImageView({
required this.netUrl,
this.imgWidth=50.0,
this.imgHeight = 50.0,
this.angleRadius = -1,//默认圆
this.defaultImgName='images/default_person.png',
this.colorBlendMode,
this.isPortrait = false,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
double defaultAngleRadius = 0;
if(angleRadius<0) {//默认圆
defaultAngleRadius = imgWidth/2.0;
} else {
defaultAngleRadius = angleRadius;
}
//ExtendedImage.network 无法加载有后缀的网络图
//http://xxxx.jpg?Expires=xxx&OSSAccessKeyId=yy
if(netUrl.contains('?')){
return Container(
width: imgWidth,
height: imgHeight,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(defaultAngleRadius)),
color: ColorUtils.cF7F7F7,
),
alignment: Alignment.center,
child: Image.network(netUrl, width: imgWidth, height: imgHeight,fit: BoxFit.fill,),
);
}
//清除内存缓存,可以调用 clearMemoryImageCache 方法
return ExtendedImage.network(
netUrl??'',
width: imgWidth,
height: imgHeight,
fit: BoxFit.fill,
cacheHeight: imgHeight.toInt(),
cacheWidth: imgWidth.toInt(),
cache: true,
color: colorBlendMode!=null ? Colors.white : null,
colorBlendMode: colorBlendMode,//color_colorBlendMode控制灰度图
shape:BoxShape.rectangle,
borderRadius: BorderRadius.all(Radius.circular(defaultAngleRadius)),
retries: 0,//重试请求的时间, 不重试
loadStateChanged: (ExtendedImageState state) {
if(state.extendedImageLoadState==LoadState.failed){
//print('图片加载失败了');
return isPortrait
?
Image.asset(
defaultImgName,
height: imgHeight,
width: imgWidth,
)
:
Container(
height: imgHeight,
width: imgWidth,
color: ColorUtils.cF7F7F7,
);
return Image.asset(//自动圆形
defaultImgName,
height: imgHeight,
width: imgWidth,
color: colorBlendMode!=null ? Colors.white : null,
colorBlendMode: colorBlendMode,//color_colorBlendMode控制灰度图
fit:BoxFit.fill,
);
}
},
//cancelToken: cancellationToken,
);
}
文字:
class XLText extends StatelessWidget {
String txt;//文字
double size;//文字大小
Color color;//文字颜色
bool isBold;//是否加粗
TextAlign align;//对齐方式
double txtHeight;//文本行高,为字体大小的倍数
int txtMaxLines;//最大行数
TextOverflow? overflow;
XLText({
required this.txt,
this.size = 14.0,
this.color=ColorUtils.c333333_60,
this.isBold=false,
this.align=TextAlign.left,
this.txtHeight = 1.1,//1.1更居中
this.txtMaxLines = 0,
this.overflow,//文字溢出处理
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
int? maxLines;
if(txtMaxLines>0){
maxLines = txtMaxLines;
}
return Text(//利用StrutStyle及height修正文字有偏移
txt??'',
textAlign:align,
overflow: overflow,
maxLines: maxLines,
strutStyle: StrutStyle(//修正字体偏下
fontSize: size,
fontWeight: isBold?FontWeight.w700:FontWeight.w400,
height: txtHeight,
),
style: TextStyle(
fontSize: size,
color: color,
fontWeight: isBold?FontWeight.w700:FontWeight.w400,
height: txtHeight,
),
);
}
}
有色圆角背景+有色文字
class XLCornerRadiusTextBgView extends StatelessWidget {
String txt;//文字
double radius;//圆角
double height;//背景高
double ?width;//背景宽
double size;//文字的font
Color bgColor;//背景颜色
Color txtColor;//文字颜色
double borderWidth;//边框宽度
Color ?borderColor;//边框颜色
Function ?onTapAction;//点击事件
XLCornerRadiusTextBgView({
required this.txt,
this.radius=10,
this.height=20,
this.width,
this.size=12.0,
this.bgColor= ColorUtils.mainColor,
this.txtColor=Colors.white,
this.borderWidth = 0,
this.borderColor,
this.onTapAction,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
double actH = height;
double actW = 0;
if(width!=null){
actW = width!;
}else{
double paintWidthWithTextStyle(TextStyle style) {
final TextPainter textPainter = TextPainter(
text: TextSpan(text: txt, style: style),
maxLines: 1,
textDirection: TextDirection.ltr)
..layout(minWidth: 0, maxWidth: double.infinity);
return textPainter.size.width;
}
actW = paintWidthWithTextStyle(TextStyle(fontWeight: FontWeight.w300, fontSize: size))+10.0;
}
return InkWell(
onTap: (){
if(onTapAction!=null){
onTapAction!(txt);
}
},
child: Container(
height: actH,
width: actW,
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(radius),
border: Border.all(color: borderColor??bgColor, width: 0)
),
child: Center(child: XLText(
txt: txt,
size: size,
align: TextAlign.center, color: txtColor,
txtMaxLines: 1,
overflow: TextOverflow.ellipsis,
),),
),
);
}
//圆角图形文字
}
防止快速点击:
Well:
class XLDebunceInkWell extends StatelessWidget {
final VoidCallback? onTap;
final Widget child;
final int milliseconds;
final BorderRadius? borderRadius;
XLDebunceInkWell(
{
required this.onTap,
required this.child,
this.milliseconds = 800,
this.borderRadius,
Key? key,
})
: super(key: key);
@override
Widget build(BuildContext context) {
return InkWell(
borderRadius: borderRadius,
onTap: onTap == null ? null : p_onTap(fun: onTap, milliseconds: milliseconds),
child: child,
);
}
final XLDebounceTextButtonTimeData timeData = XLDebounceTextButtonTimeData();
bool doubleClick(int milliseconds) {
DateTime? lastPressedAt = timeData.lastPressedAt;
if (lastPressedAt == null ||
(DateTime.now().difference(lastPressedAt) > Duration(milliseconds: milliseconds))
) {
timeData.lastPressedAt = DateTime.now();
return false;
}
return true;
}
Function() p_onTap({Function()? fun, int milliseconds = 800}) {
return () {
if (doubleClick(milliseconds)) {
return;
}
fun?.call();
};
}
}
class XLDebounceTextButtonTimeData{
DateTime? lastPressedAt;
}
TextButton:
class XLDebounceTextButton extends StatelessWidget {
final VoidCallback? onPressed;
final Widget? child;
final int milliseconds;
final VoidCallback? onLongPress;
final ValueChanged<bool>? onHover;
final ValueChanged<bool>? onFocusChange;
final ButtonStyle? style;
final FocusNode? focusNode;
final bool autofocus;
final Clip clipBehavior;
final MaterialStatesController? statesController;
XLDebounceTextButton(
{Key? key,
required this.onPressed,
required this.child,
this.milliseconds = 800,
this.onLongPress,
this.onHover,
this.onFocusChange,
this.style,
this.focusNode,
this.autofocus = false,
this.clipBehavior = Clip.none,
this.statesController})
: super(key: key);
@override
Widget build(BuildContext context) {
return TextButton(
onPressed: onPressed == null
? null
: p_onPress(fun: onPressed, milliseconds: milliseconds),
onLongPress: onLongPress,
onHover: onHover,
onFocusChange: onFocusChange,
style: style,
focusNode: focusNode,
autofocus: autofocus,
clipBehavior: clipBehavior,
statesController: statesController,
child: child!,
);
}
final XLDebounceTextButtonTimeData timeData = XLDebounceTextButtonTimeData();
bool doubleClick(int milliseconds) {
DateTime? lastPressedAt = timeData.lastPressedAt;
if (lastPressedAt == null ||
(DateTime.now().difference(lastPressedAt) > Duration(milliseconds: milliseconds))
) {
timeData.lastPressedAt = DateTime.now();
return false;
}
return true;
}
Function() p_onPress({Function()? fun, int milliseconds = 800}) {
return () {
if (doubleClick(milliseconds)) {
return;
}
fun?.call();
};
}
}
class XLDebounceTextButtonTimeData{
DateTime? lastPressedAt;
}
折叠或者不折叠的 展示View:
class XLFoldOrNotView extends StatelessWidget {
///在括号{}外
String title;
Widget ?bottomSubWidget;//底部子布局
///在括号{}内
double titleSize;//文字大小
Color titleColor;//文字色
bool isFold;//是否为折叠状态
EdgeInsetsGeometry topPadd;//顶部边距
EdgeInsetsGeometry bottomPadd;//底部边距
Function ?foldClickFun;//折叠点击事件
XLFoldOrNotView(this.title,{
this.bottomSubWidget,
this.titleSize=16,
this.titleColor = ColorUtils.c333333,
this.isFold = true,
this.topPadd = const EdgeInsets.only(top: 0,bottom: 0,left: 15, right: 15),
this.bottomPadd = const EdgeInsets.only(top: 0,bottom: 0,left: 0, right: 0),
this.foldClickFun,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Padding(padding: topPadd,
child:GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: (){
if(foldClickFun!=null){
if(isFold){//折叠了--->点击反馈为不折叠
foldClickFun!(false);
} else {//未折叠了--->点击反馈为折叠
foldClickFun!(true);
}
}
},
child: Container(//折叠头部的
color: Colors.white,
constraints: BoxConstraints(minHeight: 45),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(child: XLText(txt: title, size: titleSize, color: titleColor,),),
Padding(
padding: const EdgeInsets.only(left: 0),
child: Image.asset(
'images/${isFold?'black_arrow_down':'black_arrow_up'}.png',
height: 7,
width: 10,
),
),
],
),
),
),
),
bottomSubWidget==null
? Container()
:
(
isFold
?Container()
: Padding(padding: bottomPadd, child:bottomSubWidget!)
),
],
);
}
}
black_arrow_down.png
black_arrow_up.png
文字 + 文字 + 箭头(仅仅一行)
gray_arrow.png
class XLLeftLabCenterLabArrowView extends StatelessWidget {
BuildContext ?context;//上下文
String title;//左侧文字,标题
Color titleColor;//左侧文字颜色
double fontSize;//左侧文字大小
String starStr;//左侧文字的必填标识*星号,默认无
String desc;// 右侧文字的选择提示文字 请选择;
String value='';// 右侧文字,选择后的值
Color rigthTitlePlaceholderColor;//右侧文字未选择的颜色
Color rigthTitleColor;//右侧文字选择后的颜色
double rigthFontSize;//右侧文字大小
bool showArrow;//是否显示右侧箭头
EdgeInsetsGeometry marg;//边距
Function ?selected;//点击事件
double h;//高度
Color bgColor;//背景色
XLLeftLabCenterLabArrowView({
this.context,
required this.title,
this.titleColor = ColorUtils.c333333_60,
this.fontSize = 14.0,
this.starStr = '',
this.desc = '请选择',
this.value='',
this.rigthTitlePlaceholderColor = ColorUtils.c333333_30,
this.rigthTitleColor = ColorUtils.c333333,
this.rigthFontSize=14.0,
this.showArrow = true,
this.marg = const EdgeInsets.only(left: 15, right: 15),
this.selected,
this.h = 45,
this.bgColor = Colors.white,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
height: h,
margin: marg,
color: bgColor,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {
if(selected!=null){
selected!();
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(title,
style: TextStyle(fontSize: fontSize, color: titleColor),
strutStyle: StrutStyle(
fontSize: fontSize,
leading: 0,
height: 1.1,
// 1.1更居中
forceStrutHeight: true, // 关键属性 强制改为文字高度
),
),
Text(starStr,
style: TextStyle(fontSize: fontSize, color: Colors.red),),
],
),
const Padding(padding: EdgeInsets.only(left: 10)),
Expanded(child: Container(
child: Text.rich(
TextSpan(text: StrIsEmpty(value) ? desc: value),
style: TextStyle(
color: StrIsEmpty(value)
? rigthTitlePlaceholderColor
: rigthTitleColor,
fontSize: rigthFontSize),
strutStyle: StrutStyle(
fontSize: fontSize,
leading: 0,
height: 1.1,
// 1.1更居中
forceStrutHeight: true, // 关键属性 强制改为文字高度
),
maxLines: 1,
textAlign: TextAlign.end,
overflow: TextOverflow.ellipsis,),
),),
Offstage(
offstage: !showArrow,
child: Padding(
padding: const EdgeInsets.only(left: 8),
child: Image.asset(
'images/gray_arrow.png',
height: 12,
width: 8,
),
),
),
],
),
),
);
}
}
左右两文字(均可多行) 向下展开扩充:
/*右侧文字左对齐
XLLeftLabRigthLabView(
'某问状态:',
'aa'
marg: const EdgeInsets.only(left: 15, right: 15, top: 12.5,bottom: 12.5),
rightLabAliType:0,
),
*/
/*右侧文字右对齐,必须设置rightTxtMaxW,注意左右文字的溢出
XLLeftLabRigthLabView(
'一个名称',
'bb'
marg: const EdgeInsets.only(left: 15, right: 15, top: 12.5,bottom: 12.5),
rightTxtMaxW: ScreenW(context)-15.0*2-120,
rightLabAliType:1,
),
*/
class XLLeftLabRigthLabView extends StatelessWidget {
///在括号{}外
String title;
String desc;
///在括号{}内
double minHeight;
double leftTxtMinW;//左侧文字最小宽度
double rightTxtMaxW;//右侧文字最大宽度, rightLabAliType==0不生效
EdgeInsetsGeometry marg;//边距
double leftSize;
Color leftColor;
bool leftIsBold;
double rightSize;
Color rightColor;
bool rightIsBold;
int rightLabAliType;//右侧文字对齐方式0_在左, 1_在右
double rightToLeftPaddWhenRighttxtIsLeft;//右侧文字在左侧时距离左侧文字距离
Function ?clickFun;
XLLeftLabRigthLabView(this.title, this.desc, {
this.minHeight = 20,
this.leftTxtMinW = 85,//可自定义设置
this.rightTxtMaxW = 100,//可自定义设置ScreenW(context)-15.0*2-leftTxtMinW-10
this.marg = const EdgeInsets.only(left: 0, right: 0, top: 0,bottom: 0),
this.leftSize = 14,
this.leftColor = ColorUtils.c333333_60,
this.leftIsBold = false,
this.rightSize = 14,
this.rightColor = ColorUtils.c333333,
this.rightIsBold = false,
this.rightLabAliType = 1,//默认右侧文字右对齐
this.rightToLeftPaddWhenRighttxtIsLeft = 0,
this.clickFun,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: (){
if(clickFun!=null){
clickFun!();
}
},
child: rightLabAliType == 1
?
p_rightLabAlignOnRight()
:
p_rightLabAlignOnLeft(),
);
}
///右侧文字靠左侧
Widget p_rightLabAlignOnLeft(){
return Container(
constraints: BoxConstraints(minHeight: minHeight),
padding: marg,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, //控制文字向下扩充。不是上下对称扩充
children: <Widget>[
Visibility(
visible: !StrIsEmpty(title),
child: Container(
constraints: BoxConstraints(minWidth: leftTxtMinW),
child: Text(
title,
textAlign: TextAlign.left,
style: TextStyle(color: leftColor, fontSize: leftSize, fontWeight: leftIsBold?FontWeight.w700:FontWeight.w400),
strutStyle: StrutStyle(
fontSize: leftSize,
leading: 0,
height: 1.1,
fontWeight: leftIsBold?FontWeight.w700:FontWeight.w400,
// 1.1更居中
forceStrutHeight: true, // 关键属性 强制改为文字高度
),
),
),
),
Expanded(child: Visibility(
visible: !StrIsEmpty(desc),
child: Padding(padding: EdgeInsets.only(left: rightToLeftPaddWhenRighttxtIsLeft,),
child: Container(
child: Text.rich(
TextSpan(text: '${desc ?? ''}'),
style: TextStyle(color: rightColor, fontSize: rightSize,fontWeight: rightIsBold?FontWeight.w700:FontWeight.w400,),
strutStyle: StrutStyle(
fontSize: rightSize,
leading: 0,
height: 1.1,
fontWeight: rightIsBold?FontWeight.w700:FontWeight.w400,
// 1.1更居中
forceStrutHeight: true, // 关键属性 强制改为文字高度
),
textAlign: TextAlign.left,
//overflow: TextOverflow.ellipsis,
),
),
),
),),
],
),
);
}
///右侧文字靠右侧
Widget p_rightLabAlignOnRight(){
return Container(
constraints: BoxConstraints(minHeight: minHeight),
padding: marg,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, //控制文字向下扩充。不是上下对称扩充
children: <Widget>[
Expanded(child: Visibility(
visible: !StrIsEmpty(title),
child: Container(
constraints: BoxConstraints(minWidth: leftTxtMinW),
child: Text(
title,
textAlign: TextAlign.left,
style: TextStyle(color: leftColor, fontSize: leftSize,fontWeight: leftIsBold?FontWeight.w700:FontWeight.w400,),
strutStyle: StrutStyle(
fontSize: leftSize,
fontWeight: leftIsBold?FontWeight.w700:FontWeight.w400,
leading: 0,
height: 1.1,
// 1.1更居中
forceStrutHeight: true, // 关键属性 强制改为文字高度
),
),
),
),),
Padding(padding: const EdgeInsets.only(right: 0,),
child: Container(
constraints: BoxConstraints(maxWidth: rightTxtMaxW),
child: Text.rich(
TextSpan(text: '${desc ?? ''}'),
style: TextStyle(color: rightColor, fontSize: rightSize,fontWeight: rightIsBold?FontWeight.w700:FontWeight.w400,),
strutStyle: StrutStyle(
fontSize: rightSize,
fontWeight: rightIsBold?FontWeight.w700:FontWeight.w400,
leading: 0,
height: 1.1,
// 1.1更居中
forceStrutHeight: true, // 关键属性 强制改为文字高度
),
textAlign: TextAlign.right,
//overflow: TextOverflow.ellipsis,
),
),
),
],
),
);
}
}
文字+输入+单位文字:
(Row里面放TextField,必须把TextField用Expanded包起来,或者固定宽高)
class XLLeftLabCenterTextfieldRightLabView extends StatelessWidget {
String title;//左侧标题
Color titleColor;//左侧标题的颜色
EdgeInsetsGeometry marg;//边距
String starStr;//左侧文字的必填标识*星号,默认无
int maxLength;//最大输入长度
TextInputType keyboard;//键盘类型
String hintText;//未输入的显示文字
FocusNode ?focusNode;//输入焦点
Function ?change;//输入变化回调函数
Function ?loseFocusFun;//输入框失去焦点的回调函数
TextEditingController ?controller;//输入的控制器
List<TextInputFormatter> ?inputformats;//输入限制规则
bool readOnly;//是否为已读
String desc;//输入框后面的文字
Color descTxtColor;//输入框后面的文字颜色
Color tfTxtColor;//输入框后面的文字颜色
double textfieldWidth;//输入框宽
double textfieldHeight;//输入框高
double cellViewHeight;//整个view的heigth,大于(35 + marg的top + marg的bottom)
String tfKeySuffix;//输入框的key的后缀
XLLeftLabCenterTextfieldRightLabView({
required this.title,
this.titleColor = ColorUtils.c151515_60,
this.marg = const EdgeInsets.only(left: 15, right: 15),
this.starStr='',
this.maxLength=30,
this.focusNode ,
this.keyboard=TextInputType.text,
this.change,
this.loseFocusFun,
this.hintText='请输入',
this.controller,
this.readOnly=false,
this.desc='',//
this.descTxtColor = ColorUtils.c333333_60,
this.tfTxtColor = ColorUtils.c333333,
this.textfieldWidth = 150,
this.textfieldHeight = 35,
this.cellViewHeight = 45,
this.tfKeySuffix = 'a',
this.inputformats,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
height: cellViewHeight,
padding: marg,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
constraints: BoxConstraints(maxWidth: GlobalVariables().SCREEN_WIDTH-15*2-5-20-15-textfieldWidth),
child: XLText(txt: StrValue(title), size: 14, color: titleColor, txtMaxLines: 1,overflow: TextOverflow.ellipsis,),
),
XLText(txt: StrValue(starStr), size: 14, color: Colors.red,),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(right: 5),
child: Container(
width: textfieldWidth,
height: textfieldHeight,
child: Focus(
onFocusChange:(hasFocus){
if (hasFocus==false) {//键盘失去焦点了
// MyLog.p_org('');
if(loseFocusFun!=null){
loseFocusFun!();
}
}
},
child: TextField(
key: ObjectKey('${title}_${tfKeySuffix}'),
scrollPadding: const EdgeInsets.all(0),
textAlign: TextAlign.end,
maxLength: maxLength,
readOnly:readOnly,
keyboardType:keyboard,
maxLines: 1,
inputFormatters: inputformats,
//是否自动对焦
autofocus : false,
focusNode:focusNode!=null? focusNode:FocusNode(),
controller: controller!=null? controller:TextEditingController() ,
onChanged: (a){
if(change!=null){
change!(a);
}
},
textInputAction: TextInputAction.done,
onSubmitted: (a){
if(focusNode!=null && (focusNode?.hasFocus==true)){
focusNode!.unfocus();
}
if(change!=null){
change!(a);
}
},
style: TextStyle(fontSize: 14, color: tfTxtColor),
decoration: InputDecoration(
hintText: hintText,
hintStyle: TextStyle(
fontSize: 14,
color: ColorUtils.c444444_30),
//counter组件统计输入框文字的个数,counter仅仅是展示效果,
//不具备自动统计字数的功能,需要自己控制
counterText:'',//'${controller?.text.length}/${maxLength.toString()}',//'',//
contentPadding:EdgeInsets.only(top: 0, bottom: 0),
border: OutlineInputBorder(borderSide: BorderSide.none),
),
),
),
),
),
Visibility(
visible: !StrIsEmpty(desc),
child: Padding(
padding: const EdgeInsets.only(right: 0),
child: Container(
height: textfieldHeight,
alignment: Alignment.center,
child: XLText(
txt: desc,
color:descTxtColor,
),
),
),
),
],
),
],
),
);
}
}
文字 + 文字 + 圆角状态文字(仅仅一行):
//计算一行文字大小
Size getTextSize(String text, TextStyle style, {int maxLines=1}) {
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
maxLines: maxLines,
textDirection: TextDirection.ltr)
..layout(minWidth: 0, maxWidth: double.infinity);
return textPainter.size;
}
///
double ScreenW(BuildContext context, {bool isSubNotAppUse=false}){
if(isSubNotAppUse==true){
//使用context的宽
return MediaQuery.of(context).size.width;
} else {
double sw = GlobalVariables().SCREEN_WIDTH;
if(sw<=0.5){//获取不到
return MediaQuery.of(context).size.width;
}
return GlobalVariables().SCREEN_WIDTH;
}
}
double ScreenH(BuildContext context){
return MediaQuery.of(context).size.height;
}
class XLLeftLabCenterLabRightStatusView extends StatelessWidget {
String leftTxt;//左侧文字
Color leftTxtColor;//左侧文字颜色
double leftTxtFontSize;//左侧文字大小
double centerToLeftPadd;//中间文字与左侧文字距离
String centerTxt;//中间文字
Color centerTxtColor;//中间文字颜色
double centerTxtFontSize;//中间文字大小
//右侧状态文字
String statusTxt;//右侧状态文字
double statusSize;//右侧状态文字大小
Color statusTxtBgColor;//右侧状态文字背景色
Color statusTxtColor;//右侧状态文字颜色
EdgeInsetsGeometry marg;//边距
Function ?selected;//点击事件
XLLeftLabCenterLabRightStatusView({
required this.leftTxt,
this.leftTxtColor = ColorUtils.c333333_60,
this.leftTxtFontSize = 14.0,
this.centerToLeftPadd = 10,
required this.centerTxt,
this.centerTxtColor = ColorUtils.c333333,
this.centerTxtFontSize = 14.0,
required this.statusTxt,
this.statusSize = 10.0,
this.statusTxtBgColor = ColorUtils.mainColor,
this.statusTxtColor = Colors.white,
this.marg = const EdgeInsets.only(left: 15, right: 15, top: 5, bottom: 5),
this.selected,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
double statusW = XLCommonUtils().getTextSize(statusTxt, TextStyle(fontSize: statusSize)).width+10;
double statusH = statusSize+6;
double centerTxtMaxW = XLCommonUtils().getTextSize(centerTxt, TextStyle(fontSize: centerTxtFontSize)).width;
if(centerTxtMaxW>150) {
centerTxtMaxW = 150;
}
double leftMaxW = (ScreenW(context)-marg.horizontal-centerTxtMaxW-centerToLeftPadd-statusW);
if(leftMaxW<30){
leftMaxW = 30;
}
if(statusW>(ScreenW(context)-marg.horizontal-centerToLeftPadd-centerTxtMaxW-leftMaxW)){
statusW = (ScreenW(context)-marg.horizontal-centerToLeftPadd-centerTxtMaxW-leftMaxW);
if(statusW<=0){
statusW = 0;
}
}
return Container(
margin: marg,
child: GestureDetector(
onTap: () {
if(selected!=null){
selected!();
}
},
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
constraints: BoxConstraints(maxWidth: leftMaxW),
child: XLText(txt: leftTxt, size: leftTxtFontSize, color: leftTxtColor, txtMaxLines:1, overflow: TextOverflow.ellipsis),
),
Expanded(child: Padding(padding: EdgeInsets.only(left: centerToLeftPadd),
child: Container(
constraints: BoxConstraints(maxWidth: centerTxtMaxW),
child: Text.rich(
TextSpan(text: centerTxt),
style: TextStyle(
color: centerTxtColor,
fontSize: centerTxtFontSize,
height: 1.1,
// 1.1更居中
),
strutStyle: StrutStyle(
fontSize: centerTxtFontSize,
leading: 0,
height: 1.1,
// 1.1更居中
forceStrutHeight: true, // 关键属性 强制改为文字高度
),
maxLines: 1,
textAlign: TextAlign.left,
overflow: TextOverflow.ellipsis,),
),
),
),
Visibility(
visible: !StrIsEmpty(statusTxt),
child: Padding(padding: const EdgeInsets.only(right: 0),
child: XLCornerRadiusTextBgView(
txt: statusTxt,
bgColor: statusTxtBgColor,
txtColor: statusTxtColor,
size: statusSize,
radius: statusH/2.0,
height: statusH,
width: statusW
)
),),
],
),
),
);
}
}
文字 + 选择的按钮(仅一行):
class XLLeftLabRightSelectBtnsView extends StatelessWidget {
BuildContext context;//上下文
String title;//左侧文字,标题
Color titleColor;//左侧文字颜色
double fontSize;//左侧文字大小
String selCode;//空没选, 其他
Function clickBtnFun;//点击选择的回调
List btnLists;//选择的按钮数组
double margLeft;//左边距,参与按钮的计算
double margRight;//左边距,,参与按钮的计算
EdgeInsetsGeometry marg;//边距
double h;//高度
Color bgColor;//背景色
XLLeftLabRightSelectBtnsView({
required this.context,
required this.title,
this.titleColor = ColorUtils.c333333_60,
this.fontSize = 14.0,
required this.selCode,
required this.clickBtnFun,
required this.btnLists,
this.margLeft = 15,
this.margRight = 15,
this.marg = const EdgeInsets.only(left: 15, right: 15),
this.h = 45,
this.bgColor = Colors.white,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
height: h,
margin: marg,
color: bgColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
constraints: BoxConstraints(maxWidth: (ScreenW(context)-margLeft-margRight) * 1.0 / 3.0),
alignment: Alignment.centerLeft,
child: Text(title,
style: TextStyle(
fontSize: fontSize,
color: titleColor,
),
maxLines: 3,
strutStyle: StrutStyle(
fontSize: fontSize,
leading: 0,
height: 1.1,
// 1.1更居中
forceStrutHeight: true, // 关键属性 强制改为文字高度
),
),
),
Container(
constraints: BoxConstraints(maxWidth: (ScreenW(context)-margLeft-margRight) * 2.0 / 3.0),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
physics: const ClampingScrollPhysics(),//const ClampingScrollPhysics(),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: btnLists.map((btnM) {
bool isSel = false;
if(StrIsNotEmpty(selCode) && (selCode == MapValueStr(btnM, 'code'))) {
isSel = true;
}
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: (){
clickBtnFun(btnM);
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(width: 20,),
Image.asset(
'images/select_row_${isSel?'yes':'no'}.png',
height: 20,
width: 20,
),
Container(width: 5,),
XLText(txt: MapValueStr(btnM, 'name'), color: ColorUtils.c333333,),
],
),
);
}).toList(),
),
),
),
],
),
);
}
}
右侧放一个按钮的View:
//计算一行文字大小
Size getTextSize(String text, TextStyle style, {int maxLines=1}) {
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: style),
maxLines: maxLines,
textDirection: TextDirection.ltr)
..layout(minWidth: 0, maxWidth: double.infinity);
return textPainter.size;
}
class XLBaseViewRightBtnView extends StatelessWidget {
///在括号{}外
String title;
///在括号{}内
double minHeight;//最小高度
double titleSize;//文字大小
Color titleColor;//文字色
EdgeInsetsGeometry marg;//边距
Function ?clickHandler;
XLBaseViewRightBtnView(this.title,{
this.minHeight=45,
this.titleSize=14,
this.titleColor = ColorUtils.cF6A623,
this.marg = const EdgeInsets.only(top: 0,bottom: 0,left: 15, right: 15),
this.clickHandler,
super.key,
});
@override
Widget build(BuildContext context) {
double btnWidth = XLCommonUtils().getTextSize(title, TextStyle(fontSize: titleSize)).width;
btnWidth = (btnWidth+20);
// TODO: implement build
return Container(
color: Colors.white,
constraints: BoxConstraints(minHeight: minHeight),
padding: marg,
//color: Colors.white,
child: Row(mainAxisAlignment: MainAxisAlignment.end,children: <Widget>[
Padding(padding: const EdgeInsets.only(right: 0),
child: SizedBox(
width: btnWidth,
height: 30,
child: GestureDetector(
onTap: () {
if(clickHandler!=null){
clickHandler!(title);
}
},
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(15.0)),
color: Colors.white,
border: Border.all(
width:1,
color: titleColor,
),
),
alignment: Alignment.center,
child: Text('${title}', style: StringUtils.text_style(size: titleSize, cr: titleColor),),
),
),
),
),
],
),
);
}
}
底部0/1/2个按钮:
class XLBottomBtnsView extends StatelessWidget {
BuildContext context;
List<String> btnNames;
Function ?callBackHandler;
///true全部是操作按钮,则按橙底白字均匀铺开
bool isAllOptionBtn;
///true全部是操作按钮_按钮能否点击
bool isAllOptionBtn_btnCanOption;
XLBottomBtnsView({
required this.context,
required this.btnNames,
this.callBackHandler,
this.isAllOptionBtn = false,
this.isAllOptionBtn_btnCanOption = true,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
if(btnNames.length==0){//无按钮
return Container(
margin: const EdgeInsets.only(top:0, bottom: 0),
);
}
if(isAllOptionBtn==true){//true全部是操作按钮,则按橙底白字均匀铺开
double baseW = MediaQuery.of(context).size.width-15*2;
double btnW = (baseW-20*(btnNames.length-1))/btnNames.length;
return Container(
//color: Colors.white,
alignment:const Alignment(0, -1),
height: 50,
margin: const EdgeInsets.only(top:0, bottom: 10),
//color: Colors.white,
child: Padding(
padding: const EdgeInsets.only(top: 0,bottom: 0, left: 15, right: 15),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: btnNames.map((name) {
return SizedBox(
width: btnW,
height: 40,
child: XLDebunceInkWell(
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
onTap: () {
// print(name.toString());
if(isAllOptionBtn_btnCanOption){
if(callBackHandler!=null){
callBackHandler!(name);
}
}
},
child: Container(
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
color: isAllOptionBtn_btnCanOption?ColorUtils.mainColor:ColorUtils.c333333_60,),
alignment: Alignment.center,
child: Text('${name}', style: StringUtils.text_style(cr: ColorUtils.cFFFFFF),),
),
),
);
}).toList(),
),
),
);
}
if(btnNames.length==1){//1按钮
return Container(
//color: Colors.white,
alignment: const Alignment(0, -1),
height: 50,
margin: const EdgeInsets.only(top:0, bottom: 10),
//color: Colors.white,
child: Padding(
padding: const EdgeInsets.only(top: 0,bottom: 0, left: 15, right: 15),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: <Widget>[
SizedBox(
width: MediaQuery.of(context).size.width-15*2,
height: 40,
child: XLDebunceInkWell(
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
onTap: () {
print(btnNames[0].toString());
if(callBackHandler!=null){
callBackHandler!(btnNames[0]);
}
},
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
color: ColorUtils.mainColor),
alignment: Alignment.center,
child: Text('${btnNames[0]}', style: StringUtils.text_style(cr: Colors.white),),
),
),
),
],
),
),
);
}
if(btnNames.length>=2) { //2按钮
double baseW = MediaQuery.of(context).size.width-15*2;
double btnW = (baseW-20)/2.0;
return Container(
//color: Colors.white,
alignment: const Alignment(0, -1),
height: 50,
margin: const EdgeInsets.only(top:0, bottom: 10),
//color: Colors.white,
child: Padding(
padding: const EdgeInsets.only(top: 0,bottom: 0, left: 15, right: 15),
child: Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly,children: <Widget>[
SizedBox(
width: btnW,
height: 40,
child: XLDebunceInkWell(
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
onTap: () {
print(btnNames[0].toString());
if(callBackHandler!=null){
callBackHandler!(btnNames[0]);
}
},
child: Container(
decoration: BoxDecoration(
border: Border.all(color: ColorUtils.c444444_20, width: 1.0), // 边色与边宽度
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
color: ColorUtils.cFFFFFF,
),
alignment: Alignment.center,
child: Text('${btnNames[0]}', style: StringUtils.text_style(cr: ColorUtils.c444444_60),),
),
),
),
Expanded(child: Container(),),
SizedBox(
width: btnW,
height: 40,
child: XLDebunceInkWell(
borderRadius: const BorderRadius.all(Radius.circular(20.0)),
onTap: () {
// print(btnNames[1].toString());
if(callBackHandler!=null){
callBackHandler!(btnNames[1]);
}
},
child: Container(
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(20.0)),
color: ColorUtils.mainColor,),
alignment: Alignment.center,
child: Text('${btnNames[1]}', style: StringUtils.text_style(cr: ColorUtils.cFFFFFF),),
),
),
),
],
),
),
);
}
return Container(
margin: const EdgeInsets.only(top:0, bottom: 0),
);
}
//
}
有背景的标题:
class XLBgTitleView extends StatelessWidget{
///在括号{}外
String title;
//在括号{}内
double minHeight;//最小高度
double titleSize;//文字大小
Color titleColor;//文字色
Color bgColor;//背景色
TextAlign rightTextAlign;//文字的对齐方式
EdgeInsetsGeometry marg;//边距
XLBgTitleView(this.title,{
this.minHeight=45,
this.titleSize=14,
this.titleColor = ColorUtils.c333333,
this.bgColor = ColorUtils.c333333_05,
this.rightTextAlign = TextAlign.left,
this.marg = const EdgeInsets.only(top: 15,bottom: 15,left: 15, right: 15),
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
return Container(
color: bgColor,
constraints: BoxConstraints(minHeight: minHeight),
padding: marg,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(),
Expanded(
child: Text.rich(
TextSpan(text: '${title ?? ''}'),
style: TextStyle(color: titleColor, fontSize: titleSize),
textAlign: rightTextAlign,
//overflow: TextOverflow.ellipsis,
),
),
], //控制文字向下扩充。不是上下对称扩充
),
);
}
}
有色圆角背景+有色文字:
class XLCornerRadiusTextBgView extends StatelessWidget {
String txt;//文字
double radius;//圆角
double height;//背景高
double ?width;//背景宽
double size;//文字的font
Color bgColor;//背景颜色
Color txtColor;//文字颜色
double borderWidth;//边框宽度
Color ?borderColor;//边框颜色
Function ?onTapAction;//点击事件
XLCornerRadiusTextBgView({
required this.txt,
this.radius=10,
this.height=20,
this.width,
this.size=12.0,
this.bgColor= ColorUtils.mainColor,
this.txtColor=Colors.white,
this.borderWidth = 0,
this.borderColor,
this.onTapAction,
super.key,
});
@override
Widget build(BuildContext context) {
// TODO: implement build
double actH = height;
double actW = 0;
if(width!=null){
actW = width!;
}else{
double paintWidthWithTextStyle(TextStyle style) {
final TextPainter textPainter = TextPainter(
text: TextSpan(text: txt, style: style),
maxLines: 1,
textDirection: TextDirection.ltr)
..layout(minWidth: 0, maxWidth: double.infinity);
return textPainter.size.width;
}
actW = paintWidthWithTextStyle(TextStyle(fontWeight: FontWeight.w300, fontSize: size))+10.0;
}
return InkWell(
onTap: (){
if(onTapAction!=null){
onTapAction!(txt);
}
},
child: Container(
height: actH,
width: actW,
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(radius),
border: Border.all(color: borderColor??bgColor, width: 0)
),
child: Center(child: XLText(
txt: txt,
size: size,
align: TextAlign.center, color: txtColor,
txtMaxLines: 1,
overflow: TextOverflow.ellipsis,
),),
),
);
}
}
单独刷新的Widget:
/*
//绑定单独刷新左边的子界面的GlobalKey
GlobalKey<SingleRefreshWidgetState> refreshSRWidgetKey = GlobalKey();
SingleRefreshWidget(Container() ,key: this.refreshSRWidgetKey,);
this.refreshSRWidgetKey.currentState.refreshCurrentData();
*/
///单独刷新的Widget
class SingleRefreshWidget extends StatefulWidget {
Widget srWidget;
SingleRefreshWidget(this.srWidget,{super.key});
@override
State<SingleRefreshWidget> createState() {
// TODO: implement createState
return SingleRefreshWidgetState();
}
}
class SingleRefreshWidgetState extends State<SingleRefreshWidget> {
@override
Widget build(BuildContext context) {
// TODO: implement build
return widget.srWidget;
}
//在本Widget的refreshCurrentData方法中单独调用本Widget的setState,刷新本控件.
void refreshCurrentData() {
setState(() {
});
}
}
底部弹框选择:
typedef _ClickCallBack = void Function(int selectIndex, String selectText);
const double _cellHeight = 50.0;
const double _spaceHeight = 5.0;
const Color _spaceColor = Color(0xFFE6E6E6); //230
/*用法
XLBottomSheet.showText(
context,
dataArr: ['a', 'b', 'c'],
callFun: (int selectIndex, String selectText){
}
);
* */
class XLBottomSheet {
/**
index 从上往下 0,1,2...
*/
//弹出底部文字
static void showText(
BuildContext context,
{
required List<String> dataArr,
_ClickCallBack? callFun,
bool isShowRadius = false,//是否圆角
String title = '',//标题
})
{
List<String> _dataArr = [];
if (dataArr!=null) {
_dataArr = dataArr;
}
var titleHeight = _cellHeight;
var titltLineHeight = 0.5;
if (StrIsEmpty(title)) {
titleHeight = 0.0;
titltLineHeight = 0.0;
}
int cellNum = 5;
if(_dataArr.length<=cellNum) {
cellNum = _dataArr.length;
}
double _bgHeight = titleHeight +
titltLineHeight +
(_cellHeight+1) * cellNum +
_spaceHeight +
_cellHeight;
var _radius = isShowRadius ? 10.0 : 0.0;
showModalBottomSheet(
context: context,
//设置圆角
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(_radius),
topRight: Radius.circular(_radius),
),
),
// 抗锯齿
clipBehavior: Clip.antiAlias,
builder: (BuildContext sheCon) {
return SafeArea(
child: Container(
color: Colors.white,
height: _bgHeight,
child: Column(
children: <Widget>[
Container(
height: titleHeight,
child: Center(
child: Text(
title ?? "",
style: TextStyle(
fontSize: 13, color: Color(0xFF787878)),
textAlign: TextAlign.center,
),
),
),
SizedBox(
height: titltLineHeight,
child: Container(color: _spaceColor),
),
Container(
height: (_cellHeight) * cellNum,
child: ListView.separated(
itemCount: _dataArr.length,
shrinkWrap: true,
physics: ClampingScrollPhysics(),//NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext con, int index) {
return GestureDetector(
child: Container(
height: _cellHeight,
color: Colors.white,
child: Center(
child: Text(_dataArr[index],
style: TextStyle(
fontSize: 18,
color: Color.fromRGBO(51, 51, 51, 1.0)),
textAlign: TextAlign.center))),
// onTap: () => Navigator.of(context).pop(index),
onTap: () {
Navigator.of(sheCon).pop(index);
if (callFun != null) {
callFun(index , _dataArr[index]);
}
},
);
},
separatorBuilder: (seCon, secInx) {
return const Divider(
height: 1,
color: _spaceColor,
);
},
),
),
SizedBox(
height: _spaceHeight, child: Container(color: _spaceColor)),
GestureDetector(
child: Container(
height: _cellHeight,
color: Colors.white,
child: const Center(
child: Text("取消",
style: TextStyle(
fontSize: 18, color: Color.fromRGBO(51, 51, 51, 0.6)
),
textAlign: TextAlign.center))),
onTap: () {
Navigator.of(sheCon).pop();
},
),
],
),
));
});
}
}
底部弹框--多选:
/*
*用法:
XLBottomMultiSelectSheet.showText(
context,
dataArr: ['a', 'b', 'c'],
callFun: (List<int> selInxs, List<String>selTxts){
}
);
* */
typedef _ClickCallBack = void Function(List<int> selInxs, List<String>selTxts);
const double _cellHeight = 50.0;
const double _spaceHeight = 5.0;
const Color _spaceColor = Color(0xFFE6E6E6); //230
class XLBottomMultiSelectSheet {
/**
index 从上往下 0,1,2...
*/
//弹出底部文字
static void showText(
BuildContext context,
{
required List<String> dataArr,
_ClickCallBack? callFun,
bool isShowRadius = false,//是否圆角
String title = '',//标题
})
{
List<Map> _dataArr = [];
if (dataArr!=null) {
for(dynamic obj in dataArr) {
Map item = {
'name':StrValue(obj),
'xl_isSel':false
};
_dataArr.add(item);
}
}
var titleHeight = _cellHeight;
var titltLineHeight = 0.5;
if (StrIsEmpty(title)) {
titleHeight = 0.0;
titltLineHeight = 0.0;
}
int cellNum = 5;
if(_dataArr.length<=cellNum) {
cellNum = _dataArr.length;
}
double _bgHeight = titleHeight +
titltLineHeight +
(_cellHeight+1) * cellNum +
_spaceHeight +
_cellHeight;
var _radius = isShowRadius ? 10.0 : 0.0;
StateSetter _aState;
showModalBottomSheet(
context: context,
//设置圆角
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(_radius),
topRight: Radius.circular(_radius),
),
),
// 抗锯齿
clipBehavior: Clip.antiAlias,
builder: (BuildContext sheCon) {
return SafeArea(
child: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
_aState = setState;
// _aState(() {});
return Container(
color: Colors.white,
height: _bgHeight,
child: Column(
children: <Widget>[
Container(
height: titleHeight,
child: Center(
child: Text(
title ?? "",
style: const TextStyle(
fontSize: 13, color: Color(0xFF787878)),
textAlign: TextAlign.center,
),
),
),
SizedBox(
height: titltLineHeight,
child: Container(color: _spaceColor),
),
Container(
height: (_cellHeight) * cellNum,
child: ListView.separated(
itemCount: _dataArr.length,
shrinkWrap: true,
physics: ClampingScrollPhysics(),//NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext con, int index) {
bool cell_xl_isSel = MapValuebool(_dataArr[index], 'xl_isSel');
return GestureDetector(
child: Container(
height: _cellHeight,
color: Colors.white,
child: (
cell_xl_isSel
?
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
XLText(txt: _dataArr[index]['name'], size: 18, color: ColorUtils.mainColor,),
Container(width: 15,),
const Icon(
Icons.check_sharp,
color: ColorUtils.mainColor,
size: 18.0,
)
],
)
:
Center(
child: XLText(txt:_dataArr[index]['name'], size: 18, color: ColorUtils.c333333),
)
),
),
onTap: () {
bool xl_isSel = MapValuebool(_dataArr[index], 'xl_isSel');
xl_isSel = !xl_isSel;
_dataArr[index]['xl_isSel'] = xl_isSel;
_aState(() {});
},
);
},
separatorBuilder: (seCon, secInx) {
return const Divider(
height: 1,
color: _spaceColor,
);
},
),
),
SizedBox(
height: _spaceHeight, child: Container(color: _spaceColor)),
GestureDetector(
child: Container(
height: _cellHeight,
color: Colors.white,
child: const Center(
child: Text("确定",
style: TextStyle(
fontSize: 18, color: ColorUtils.c333333_60
),
textAlign: TextAlign.center))),
onTap: () {
Navigator.of(sheCon).pop();
if (callFun != null) {
List<int> mySelInxs = <int>[];
List<String> mySelTxts = <String>[];
for(int a=0; a<_dataArr.length; a++) {
bool xl_isSel = MapValuebool(_dataArr[a], 'xl_isSel');
if(xl_isSel){
mySelInxs.add(a);
mySelTxts.add(MapValueStr(_dataArr[a], 'name'));
}
}
if(callFun!=null){
callFun(mySelInxs, mySelTxts);
}
}
},
),
],
),
);
},
),
);
});
}
}
网友评论