getPositionForChild的返回值控制显示位置
getConstraintsForChild的返回值控制显示的宽高
在shouldRelayout中根据需要重绘界面
class _DropDownMenuRouteLayout extends SingleChildLayoutDelegate {
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
// TODO: implement getConstraintsForChild
return BoxConstraints.loose(Size(constraints.biggest.width, 300));
}
@override
Offset getPositionForChild(Size size, Size childSize) {
// TODO: implement getPositionForChild
return Offset(0, 0);
}
@override
bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
// TODO: implement shouldRelayout
return true;
}
}
```
class _DropDownMenuRoute extends PopupRoute {
@override
// TODO: implement barrierColor
Color get barrierColor => null;
@override
// TODO: implement barrierDismissible
bool get barrierDismissible => true;
@override
// TODO: implement barrierLabel
String get barrierLabel => null;
@override
Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) {
// TODO: implement buildPage
return CustomSingleChildLayout(
delegate: _DropDownMenuRouteLayout(),
child: Container(
color: Color(0xffff0000),
),
);
}
@override
// TODO: implement transitionDuration
Duration get transitionDuration => Duration(milliseconds: 300);
}
可以看到,下拉列表的显示位置是从左上角位置开始显示的,高度被设置为了300,并把触发下拉列表的按钮覆盖了
在触发下拉的按钮的点击事件中,获取该按钮的显示区域,即左、上、右、下的值
onTap: () {
RenderBox renderBox = _globalKey.currentContext.findRenderObject();
Rect box = renderBox.localToGlobal(Offset.zero) & renderBox.size;
print(box);
}
结果为:
Rect.fromLTRB(0.0, 80.0, 360.0, 124.0)
要使下拉列表显示在触发下拉按钮的下方,且宽度相等,只需要将getPositionForChild的返回值修改为Offset(0, box.bottom),getConstraintsForChild的返回值修改为BoxConstraints.loose(Size(box.right - box.left, 传入的高度))即可
在_DropDownMenuRoute和_DropDownMenuRouteLayout中新增如下两个可选参数,并修改getPositionForChild和getConstraintsForChild方法的返回值
Rect position 控制下拉列表的位置
double menuHeight 控制下拉列表的高度
class _DropDownMenuRoute extends PopupRoute {
final Rect position;
final double menuHeight;
_DropDownMenuRoute({this.position, this.menuHeight});
...
@override
Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) {
// TODO: implement buildPage
return CustomSingleChildLayout(
delegate: _DropDownMenuRouteLayout(position: position, menuHeight: menuHeight),
child: Container(
color: Color(0xffff0000),
),
);
}
...
}
class _DropDownMenuRouteLayout extends SingleChildLayoutDelegate {
final Rect position;
final double menuHeight;
_DropDownMenuRouteLayout({this.position, this.menuHeight});
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
// TODO: implement getConstraintsForChild
return BoxConstraints.loose(Size(position.right - position.left, 300));
}
@override
Offset getPositionForChild(Size size, Size childSize) {
// TODO: implement getPositionForChild
return Offset(0, position.bottom);
}
...
}
在打开下拉菜单时,将按钮的显示区域和预设高度(例如300)传入即可
onTap: () {
RenderBox renderBox = _globalKey.currentContext.findRenderObject();
Rect box = renderBox.localToGlobal(Offset.zero) & renderBox.size;
print(box);
Navigator.push(context, _DropDownMenuRoute(position: box, menuHeight: 300));
}
class _DropDownMenuRoute extends PopupRoute {
...
@override
Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) {
// TODO: implement buildPage
return CustomSingleChildLayout(
delegate: _DropDownMenuRouteLayout(position: position, menuHeight: menuHeight),
child: SizeTransition(
sizeFactor: Tween(
begin: 0.0,
end: 1.0
).animate(animation),
child: Container(
color: Color(0xffff0000),
),
),
);
}
...
}
import 'package:flutter/material.dart';
class DropDownMenu extends StatelessWidget {
GlobalKey _globalKey = GlobalKey();
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
appBar: AppBar(
title: Text("DropDownMenu"),
),
body: Center(
heightFactor: 1.0,
child: Container(
key: _globalKey,
height: 44,
decoration: BoxDecoration(
border: Border.all(
color: Color(0xffe6e6e6),
width: 1.0
)
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
RenderBox renderBox = _globalKey.currentContext.findRenderObject();
Rect box = renderBox.localToGlobal(Offset.zero) & renderBox.size;
print(box);
Navigator.push(context, _DropDownMenuRoute(position: box, menuHeight: 300));
},
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
"空间类型",
style: TextStyle(
fontSize: 16,
color: Color(0xff333333)
),
),
Icon(Icons.arrow_drop_down, size: 24, color: Color(0xffe6e6e6))
],
),
),
),
),
),
);
}
}
class _DropDownMenuRouteLayout extends SingleChildLayoutDelegate {
final Rect position;
final double menuHeight;
_DropDownMenuRouteLayout({this.position, this.menuHeight});
@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
// TODO: implement getConstraintsForChild
return BoxConstraints.loose(Size(position.right - position.left, 300));
}
@override
Offset getPositionForChild(Size size, Size childSize) {
// TODO: implement getPositionForChild
return Offset(0, position.bottom);
}
@override
bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {
// TODO: implement shouldRelayout
return true;
}
}
class _DropDownMenuRoute extends PopupRoute {
final Rect position;
final double menuHeight;
_DropDownMenuRoute({this.position, this.menuHeight});
@override
// TODO: implement barrierColor
Color get barrierColor => null;
@override
// TODO: implement barrierDismissible
bool get barrierDismissible => true;
@override
// TODO: implement barrierLabel
String get barrierLabel => null;
@override
Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) {
// TODO: implement buildPage
return CustomSingleChildLayout(
delegate: _DropDownMenuRouteLayout(position: position, menuHeight: menuHeight),
child: SizeTransition(
sizeFactor: Tween(
begin: 0.0,
end: 1.0
).animate(animation),
child: Container(
color: Color(0xffff0000),
),
),
);
}
@override
// TODO: implement transitionDuration
Duration get transitionDuration => Duration(milliseconds: 300);
}