likes
comments
collection
share

Flutter 输入弹窗 封装

作者站长头像
站长
· 阅读数 7

前因

开发中常用的弹窗封装,这次封装的是可输入的弹窗,支持标题和输入框可选。 预计效果是这个样子:

Flutter 输入弹窗 封装

分析

整个功能分拆:

  1. 头部的提示消息
  2. 中间的输入框
  3. 下面的分割线
  4. 两个按钮

上面的文字和输入框可以看做一个整体,使用Padding包裹后进行分区,下面的分割线和按钮可以用Row包裹。

设计属性:

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;//圆角,输入框圆角和弹窗圆角共用

由于文本需要自适应高度,所以这里先封装一个计算文本高度的函数:

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;
}

实现部分代码:

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;
}

使用函数封装:

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;
}

最终效果:

  1. 既有输入框亦有提示语

Flutter 输入弹窗 封装

  1. 仅提示语

Flutter 输入弹窗 封装 3. 仅输入框

Flutter 输入弹窗 封装