1

在移动开发中,下拉弹框是一种很常见的选择交互方式,效果如下图所示。
在这里插入图片描述
对于这种弹框,我们可以使用Dialog来实现,下面是自定义弹框的主要代码。

Color _bgColor = Colors.white;
double cellHeight = 34;
double cellWidth=120;

typedef ClickCallBack = void Function(int selectIndex, String selectText);

class PopMenus {
  static void showPop(
      {@required BuildContext context,
      @required List<String> listData,
      @required String selText,
      ClickCallBack clickCallback}) {
    Widget _buildMenuLineCell(dataArr) {
      return ListView.separated(
        itemCount: dataArr.length,
        physics: const NeverScrollableScrollPhysics(),
        itemBuilder: (BuildContext context, int index) {
          return GestureDetector(
              onTap: () {
                Navigator.pop(context);
                if (clickCallback != null) {
                  clickCallback(index, listData[index]);
                }
              },
              child: Container(
                height: cellHeight,
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    selText==dataArr[index]?
                    Text(dataArr[index], style: TextStyle(fontSize: 16,color:
                    Colors.blue)):Text(dataArr[index], style: TextStyle(fontSize: 16))
                  ],
                ),
              ));
        },
        separatorBuilder: (context, index) {
          return Divider(
            height: 0.1,
            color: Color(0xFFE6E6E6),
          );
        },
      );
    }

    _buildMenusView(dataArr) {
      var cellH = dataArr.length * cellHeight;
      var navH = ScreenUtils.navigationBarHeight;
      navH = navH - ScreenUtils.topSafeHeight;
      var leftP=(ScreenUtils.screenWidth-cellWidth)/2;
      return Positioned(
        left: leftP,
        top: navH-10,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          children: <Widget>[
            Container(
              padding: EdgeInsets.only(right: 10),
              child: TriangleUpWidget(height: 10,width: 14),
            ),
            ClipRRect(
                borderRadius: BorderRadius.circular(2),
                child: Container(
                    color: _bgColor,
                    width: cellWidth,
                    height: cellH,
                    child: _buildMenuLineCell(dataArr)))
          ],
        ),
      );
    }

    showDialog(
        context: context,
        barrierDismissible: false,
        builder: (context) {
          return BasePopMenus(child: _buildMenusView(listData));
        });
  }
}

class BasePopMenus extends Dialog {
  BasePopMenus({
    Key key,
    this.child,
  }) : super(key: key);

  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Material(
      type: MaterialType.transparency,
      child: Stack(
        fit: StackFit.expand,
        children: <Widget>[
          GestureDetector(onTap: () => Navigator.pop(context)),
          child
        ],
      ),
    );
  }
}

如果需要改变弹框的位置,可以修改_buildMenusView()方法中的Positioned组件的边距代码。上面的代码中用到了一个自定义三角形,代码如下。

class TriangleUpPainter extends CustomPainter {

  Color color; //填充颜色
  Paint _paint; //画笔
  Path _path; //绘制路径
  double angle; //角度

  TriangleUpPainter() {
    _paint = Paint()
      ..strokeWidth = 1.0 //线宽
      ..color = Colors.white
      ..isAntiAlias = true;
    _path = Path();
  }

  @override
  void paint(Canvas canvas, Size size) {
    final baseX = size.width;
    final baseY = size.height;
    //起点
    _path.moveTo(baseX*0.5, 0);
    _path.lineTo(baseX, baseY);
    _path.lineTo(0, baseY);
    canvas.drawPath(_path, _paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }
}

class TriangleUpWidget extends StatefulWidget {
  double height;
  double width;

  TriangleUpWidget({Key key, this.height = 14, this.width = 16}) : super(key:
  key);

  @override
  CoreTriangleState createState() => CoreTriangleState();
}

class CoreTriangleState extends State<TriangleUpWidget> {
  @override
  Widget build(BuildContext context) {
    return Container(
        height: widget.height,
        width: widget.width,
        child: CustomPaint(
          painter: TriangleUpPainter(),
        ));
  }
}

最后,在需要弹框的地方,调用我们自定义的弹框组件即可,如下所示。

PopMenus.showPop(context: context, listData: segmentLists,
      selText: selectedTab, clickCallback: (int index, String value){
                  
 });

xiangzhihong
5.9k 声望15.3k 粉丝

著有《React Native移动开发实战》1,2,3、《Kotlin入门与实战》《Weex跨平台开发实战》、《Flutter跨平台开发与实战》1,2和《Android应用开发实战》