美文网首页
Flutter 常用控件

Flutter 常用控件

作者: 雨_田 | 来源:发表于2024-07-01 02:34 被阅读0次

    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);
                              }
    
                            }
                          },
                        ),
                      ],
                    ),
                  );
                },
    
              ),
    
    
          );
        });
      }
    }
    

    相关文章

      网友评论

          本文标题:Flutter 常用控件

          本文链接:https://www.haomeiwen.com/subject/qkcmcjtx.html