前因
开发中常用的弹窗封装,这次封装的是可输入的弹窗,支持标题和输入框可选。 预计效果是这个样子:
分析
整个功能分拆:
- 头部的提示消息
- 中间的输入框
- 下面的分割线
- 两个按钮
上面的文字和输入框可以看做一个整体,使用Padding包裹后进行分区,下面的分割线和按钮可以用Row包裹。
设计属性:
dart
final String cancelTit;//取消按钮标题
final String sureTit;确定按钮标题
final String? message;//提示消息
final String? inputText;//输入框已输入文字
final String? placeholder;//输入框提示语
final bool isOnlyMsg;//是否仅提示消息
final ValueChanged<String>? onTextChange;//输入框文本改变
final ValueChanged<bool>? onClick;//点击事件
final ValueChanged<String>? onEditComplete;//输入完成
final GestureTapCallback? onTap;//手势点击,主要目的是让键盘消失
final Color? msgColor;//消息文本颜色
final Color? inputTextColor;//如数文本颜色
final Color? bgColor;//弹窗背景色
final Color? inputBgColor;//输入框背景色
final Color? lineColor;//分割线颜色
final Color? cancelTitColor;//取消标题颜色
final Color? sureTitColor;//确定标题颜色
final Color? placeholderColor;//提示输入文本颜色
final double? fontSize;//消息字体大小
final double? inputFontSize;//输入字体大小
final double? buttonFontSize;//按钮字体大小
final String? fontFamily;//字体
final FontWeight? fontWeight;//字重
final TextAlign? textAlign;//文本对齐
final EdgeInsets? edgeInsets;//文本和输入框边距
final int? maxLength;//最长输入长度
final double? connerRadius;//圆角,输入框圆角和弹窗圆角共用
由于文本需要自适应高度,所以这里先封装一个计算文本高度的函数:
arduino
static Size boundingTextSize(
String text,//文本
TextStyle textStyle,//文本样式
{int maxLines = 2^31,//最多行数
double maxWidth = double.infinity,//最大宽度
TextDirection textDirection = TextDirection.ltr,//文本方向
}){
if (text == null || text.isEmpty){
return Size.zero;
}
final TextPainter textPainter = TextPainter(
textDirection: textDirection,
text: TextSpan(
text: text,
style: textStyle,
),
maxLines: maxLines
)..layout(maxWidth: maxWidth);
return textPainter.size;
}
实现部分代码:
less
FocusNode boardFocus = FocusNode();
double spaceTopDefault = 15;//默认垂直间距
double contentHeight = 51;//按钮高度和线
double cornerRadius = 8;//默认圆角
double buttonHeight = 50;//按钮高度
double textFieldHeight = 40;//输入框高度
double leftAndRightSpace = 30;//左右间距
double width = double.infinity;
double heightMsg = 0;
@override
void initState() {
// TODO: implement initState
super.initState();
//根据情况判断边距确定头部间距
if (widget.edgeInsets != null){
spaceTopDefault = widget.edgeInsets!.top;
leftAndRightSpace = widget.edgeInsets!.left + widget.edgeInsets!.right;
}
//判断是否仅展示文本
if (!widget.isOnlyMsg){
contentHeight += textFieldHeight;
}
contentHeight += spaceTopDefault * 2;
if (widget.connerRadius != null){
cornerRadius = widget.connerRadius!;
}
}
@override
Widget build(BuildContext context) {
// TODO: implement build
//获取屏幕宽度
width = MediaQuery.of(context).size.width * 0.8;
double height = contentHeight;
if (widget.message != null){
//计算文本尺寸
Size size = UIEngineTool.boundingTextSize(
widget.message!,
TextStyle(
fontSize: widget.fontSize,
fontWeight: widget.fontWeight,
fontFamily: widget.fontFamily,
),
maxWidth: width - leftAndRightSpace,
);
heightMsg = size.height;
if (widget.isOnlyMsg){
height += heightMsg;
}else{
height += heightMsg + spaceTopDefault;
}
}
return GestureDetector(
onTap: (){
FocusScope.of(context).requestFocus(boardFocus);
},
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(widget.connerRadius??cornerRadius)),
child: Container(
constraints: BoxConstraints(
maxWidth: width,
),
height: height,
alignment: Alignment.center,
color: widget.bgColor,
child: Column(
//获取需要展示的Widget
children: getListWidget(),
),
),
),
);
}
List<Widget> getListWidget(){
List<Widget> widgets = [];
//如果仅显示文本
if (widget.isOnlyMsg){
Padding padding = Padding(
padding: widget.edgeInsets ?? EdgeInsets.all(spaceTopDefault),
child: SizedBox(
width: double.infinity,
height: heightMsg,
child: SVText(
text: widget.message!,
textColor: widget.msgColor,
textAlign: widget.textAlign,
fontSize: widget.fontSize,
fontWeight: widget.fontWeight,
fontFamily: widget.fontFamily,
),
),
);
widgets.add(padding);
}else if (widget.message != null){//仅显示输入框
Padding padding = Padding(
padding: widget.edgeInsets ?? EdgeInsets.all(spaceTopDefault),
child: Column(
children: [
SizedBox(
width: double.infinity,
height: heightMsg,
child: SSLText(//封装的基础文本,可用Text替代
text: widget.message!,
textColor: widget.msgColor,
textAlign: widget.textAlign,
fontSize: widget.fontSize,
fontWeight: widget.fontWeight,
fontFamily: widget.fontFamily,
),
),
Padding(padding: EdgeInsets.only(top: spaceTopDefault)),
SizedBox(
height: textFieldHeight,
width: double.infinity,
child: SSLTextField(//封装的基础输入框,可用TextField替代
text: widget.inputText,
placeholder: widget.placeholder,
fontSize: widget.inputFontSize,
textColor: widget.inputTextColor,
bgColor: widget.inputBgColor,
placeholderColor: widget.placeholderColor,
onTextChange: widget.onTextChange,
onEditComplete: widget.onEditComplete,
cornerRadius: cornerRadius,
maxLines: 1,
maxLength: widget.maxLength,
),
),
],
)
);
widgets.add(padding);
}else{
Padding padding = Padding(
padding: widget.edgeInsets ?? EdgeInsets.all(spaceTopDefault),
child: SizedBox(
height: textFieldHeight,
width: double.infinity,
child: SSLTextField(
text: widget.inputText,
placeholder: widget.placeholder,
fontSize: widget.inputFontSize,
textColor: widget.inputTextColor,
bgColor: widget.inputBgColor,
placeholderColor: widget.placeholderColor,
onTextChange: widget.onTextChange,
onEditComplete: widget.onEditComplete,
cornerRadius: cornerRadius,
maxLines: 1,
maxLength: widget.maxLength,
),
),
);
widgets.add(padding);
}
//中间分隔线
Container lineV = Container(
color: widget.lineColor,
height: 1,
width: double.infinity,
child: const Padding(padding: EdgeInsets.zero),
);
widgets.add(lineV);
Container box = Container(
height: buttonHeight,
color: widget.bgColor,
width: double.infinity,
child: Row(
children: [
Expanded(
flex: 1,
child:TextButton(
onPressed: (){
if (widget.onClick != null){
widget.onClick!(false);
}
Navigator.of(context).pop(false);
},
child: SSLText(
text: widget.cancelTit,
textColor: widget.cancelTitColor,
fontSize: widget.buttonFontSize,
fontFamily: widget.fontFamily,
fontWeight: widget.fontWeight,
),
)
),
Container(
height: double.infinity,
width: 1,
color: widget.lineColor,
child: const Padding(padding: EdgeInsets.zero),
),
Expanded(
flex: 1,
child:TextButton(
onPressed: (){
if (widget.onClick != null){
widget.onClick!(true);
}
Navigator.of(context).pop(true);
},
child: SSLText(
text: widget.sureTit,
textColor: widget.sureTitColor,
fontSize: widget.buttonFontSize,
fontFamily: widget.fontFamily,
fontWeight: widget.fontWeight,
),
)
),
],
),
);
widgets.add(box);
return widgets;
}
使用函数封装:
php
static Future<bool> showInputAlert(
BuildContext context,
{ required String cancelTit,
required String sureTit,
ValueChanged<String>? onTextChange,
ValueChanged<String>? onEditComplete,
ValueChanged<bool>? onClick,
String? message,
String? inputText,
bool? isOnlyMsg,
String? placeholder,
GestureTapCallback? onTap,
Color? msgColor,
Color? inputTextColor,
Color? bgColor,
Color? inputBgColor,
Color? lineColor,
Color? cancelTitColor,
Color? sureTitColor,
Color? placeholderColor,
double? fontSize,
double? inputFontSize,
double? buttonFontSize,
String? fontFamily,
FontWeight? fontWeight,
TextAlign? textAlign,
EdgeInsets? edgeInsets,
int? maxLength,
double? cornerRadius,
})async {
var result = await showDialog(context: context, builder: (context){
return Dialog(//这里采用Dialog,其他的布局会有一些自带的样式
child: SSLInputAlert(
cancelTit: cancelTit,
sureTit: sureTit,
message: message,
isOnlyMsg: isOnlyMsg??false,
inputText: inputText,
placeholder: placeholder,
placeholderColor: placeholderColor,
onTextChange: onTextChange,
onClick: onClick,
onEditComplete: onEditComplete,
onTap: onTap,
msgColor: msgColor,
inputTextColor: inputTextColor,
bgColor: bgColor,
inputBgColor: inputBgColor,
lineColor: lineColor,
cancelTitColor: cancelTitColor,
sureTitColor: sureTitColor,
fontSize: fontSize,
inputFontSize: inputFontSize,
buttonFontSize: buttonFontSize,
fontFamily: fontFamily,
fontWeight: fontWeight,
textAlign: textAlign,
edgeInsets: edgeInsets,
maxLength: maxLength,
connerRadius: cornerRadius,
),
);
});
if (result == null){
return false;
}
return result;
}
最终效果:
- 既有输入框亦有提示语
- 仅提示语
3. 仅输入框