Flutter 输入弹窗 封装

前因

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

分析

整个功能分拆:

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

上面的文字和输入框可以看做一个整体,使用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;
}

最终效果:

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

3. 仅输入框

相关推荐
iFlyCai8 小时前
Xcode 16 pod init失败的解决方案
ios·xcode·swift
sunly_10 小时前
Flutter:父组件,向子组件传值,子组件向二级页面传值
flutter
爱学习的绿叶13 小时前
flutter TabBarView 动态添加删除页面
flutter
趴菜小玩家15 小时前
使用 Gradle 插件优化 Flutter Android 插件开发中的 Flutter 依赖缺失问题
android·flutter·gradle
郝晨妤17 小时前
HarmonyOS和OpenHarmony区别是什么?鸿蒙和安卓IOS的区别是什么?
android·ios·harmonyos·鸿蒙
Hgc5588866618 小时前
iOS 18.1,未公开的新功能
ios
CocoaKier19 小时前
苹果商店下载链接如何获取
ios·apple
zhlx283521 小时前
【免越狱】iOS砸壳 可下载AppStore任意版本 旧版本IPA下载
macos·ios·cocoa
XZHOUMIN1 天前
网易博客旧文----编译用于IOS的zlib版本
ios
爱吃香菇的小白菜1 天前
H5跳转App 判断App是否安装
前端·ios