在移动开发中,下拉弹框是一种很常见的选择交互方式,效果如下图所示。
对于这种弹框,我们可以使用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 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: [
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: [
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: [
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 {
@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){
});