前因

项目开发中总有一些常用的界面,需求能够重复运用,于是,有了这篇文章,期望作用如下:

Flutter 仿iOS Sheet Action 弹窗封装

需求分析

  1. 底部撤销按钮,字体色彩独自显现
  2. 选项按钮并排显现,可能存在多个
  3. 挑选后需求有对应的成果回来
  4. 整个弹窗背景色通明

根据以上需求,规划组件需求开放的属性如下:

final String cancelTitle;//撤销按钮标题
final String? title;//提示标题,此版本暂未完成显现标题
final List<String> actions;//选项按钮标题
final Color? cancelColor;//撤销按钮色彩
final Color? titleColor;//标题色彩
final Color? actionColor;//选项标题色彩
final Color bgColor;//选项卡背景色
final String? fontFamily;//字体
final double fontSize;//字号
final double  selectionHeight;//每个选项的高度
final double borderRadius;//圆角巨细
final FontWeight fontWeight;//字重
final ValueChanged<int>? selectAction;//选项成果回调

作用完成

拆分模块:

  1. 整体运用一个SizeBox包装,控制组件巨细
  2. 整体展现方式为列表,先用一个Column包装
  3. 考虑到上层选项卡共用一个圆角和基层选项卡独自一个圆角,将运用两个Column将上基层拆分
  4. 圆角作用则用ClipRRect包裹
  5. 每个选项卡选中事情则直接用GestureDetector包装,回来点击事情
  6. 考虑到每个选项卡有背景色,一切用Container包裹一下,展现背景色
  7. 文字显现部分则直接运用Text这儿用的SVText是对Text组件的封装,是为了便于开发和后期的修正,其本质便是一个Text,开放了一些常用属性。

上代码:

Widget build(BuildContext context) {
  // TODO: implement build
  double width =  MediaQuery.of(context).size.width*0.9;//计算需求展现的宽度
  double safeBottomHeight = MediaQuery.of(context).padding.bottom;//底部需求留出安全区的位置
  double space = 10;//撤销选项和其他选项的距离
  return SizedBox(//运用SizeBox进行包装
      width: width,//宽
      height: (selectionHeight + 1)*(actions.length + 1) + safeBottomHeight + space,//高
      child: Column(//分层
        children: [
          ClipRRect(//上层圆角
            borderRadius: BorderRadius.all(Radius.circular(borderRadius)),//圆角
            child: Column(//上层显现按钮
            //这儿为了减少代码量,则直接将生成选项卡的代码逻辑封装了一下
              children: getActionSheet(context),
            ),
          ),
          Padding(//上层和撤销选项距离
              padding: EdgeInsets.only(top: space),
          ),
          ClipRRect(//基层圆角
            borderRadius: BorderRadius.all(Radius.circular(borderRadius)),
            //封装选项卡生成函数
            child: getSelectWidget(cancelTitle, true, -1,context),
          ),
          Padding(
              padding: EdgeInsets.only(top: safeBottomHeight),
          ),
        ],
      )
  );
}
//回来上层选项卡一切的视图
List<Widget> getActionSheet(BuildContext context){
  List<Widget>  actionsWidget = [];
  for (int i = 0;i < actions.length; i ++){
    String title = actions[i];
    //生成上层每个选项卡
    GestureDetector action = getSelectWidget(title, false, i, context);
    actionsWidget.add(action);
    if (i < actions.length - 1){
      Padding padding = const Padding(
          padding: EdgeInsets.only(top: 1)
      );
      actionsWidget.add(padding);
    }
  }
  return actionsWidget;
}
//选项卡生成函数
GestureDetector getSelectWidget(String title, bool isCancel, int tag, BuildContext context){
  return GestureDetector(//点击事情包裹
    child: Container(
      height: selectionHeight,
      width: double.infinity,
      color: bgColor,
      alignment: Alignment.center,
      child: SSLText(
        text: title,
        textColor: isCancel?cancelColor:actionColor,
        fontSize: fontSize,
        fontWeight: fontWeight,
        textDecoration: TextDecoration.none,
      ),
    ),
    onTap: (){
    //点击回调事情
      if (selectAction != null){
        selectAction!(tag);
      }
      //由于选用的是弹窗方式,则可运用pop获取回来值
      Navigator.of(context).pop(tag);
    },
  );
}

至此弹窗根本封装结束。由于现在未涉及到运用标题的弹窗,标题功能暂未开发。

弹窗运用

弹窗封装完成后还需求有函数去展现这个弹窗,这儿参考Flutter 对话框Flutter 弹窗款式。 最后挑选运用showCupertinoModalPopup,这个弹窗的作用和iOS类似,只需将封装好的弹窗传递过去就好,剩下的动画作用和回来值系统现已处理好,直接用就行了。

上代码:

static Future<int> showBottomSheet(BuildContext context,
    { required List<String> actions,
      required String cancelTitle,
      Color? titColor,
      Color? cancelColor,
      Color? actionColor,
      double? fontSize,
      String? fontFamily,
      double? selectionHeight,
      double? borderRadius,
      Color? bgColor,
      String? title,
      ValueChanged? selection,
    })async {
  //通过系统获取回来值,具体的能够研讨下弹窗的原理,这个便是获取上面pop的回来值
  var result = await showCupertinoModalPopup(context: context, builder: (context){
    return SSLSheetAlert(//封装好的弹窗
      actions: actions,
      cancelTitle: cancelTitle,
      selectAction: selection,
      titleColor: titColor,
      cancelColor: cancelColor,
      actionColor: actionColor,
      fontSize: fontSize ?? 16,
      fontFamily: fontFamily,
      selectionHeight: selectionHeight??50,
      borderRadius: borderRadius??8,
      bgColor: bgColor??Colors.white,
      title: title,
    );
  });
  //需求额定处理的是,假如直接点击背景罩,此刻回来的是null,所以这儿需求特殊判断一下
  if (result == null){
    return -1;
  }
  return result;
}

终究用法:

//获取系统回来成果
int result = await UIAlertTool.svShowBottomSheet(
    context,
    actions:["男", "女"],
    cancelTitle: "撤销",
    actionColor: Colors.green,
    cancelColor: Colors.red,
    selection: (value){
    获取回调成果
      debugPrint("ssl get result 11 $value");
    }
);
debugPrint("ssl get result $result");

终究作用:

iOS

Flutter 仿iOS Sheet Action 弹窗封装

Android

Flutter 仿iOS Sheet Action 弹窗封装

注意:

在没有运用Material的时分,Text文本显现会和运用Material有差异,说下两个点:

  1. 没有运用的时分文本下面会有黄色的下划线,此刻需求运用TextStyle中的decoration属性,设置为TextDecoration.none
  2. 字重在没有运用Material的时分会加粗,这个时分需求设置一下字重,通常设置为FontWeight.w500