美文网首页
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