[英]How to get the exact size of PopupMenuButton (or any widget)?
I need to get the PopupMenuButton
's size because it has a property offset
which controls where the dropdown menu is rendered on the screen, and I want this one to be rendered such that the top left of the drop down menu is aligned with the bottom left of the PopupMenuButton
(see image below).我需要获取PopupMenuButton
的大小,因为它有一个属性offset
,它控制下拉菜单在屏幕上的呈现位置,我希望呈现这个菜单,使得下拉菜单的左上角与底部对齐PopupMenuButton
的左侧(见下图)。
My approach now is this:我现在的做法是这样的:
extension TextExtension on Text {
/// Calculates the size of the text inside this text widget.
/// note: this method supposes ltr direction of text, which is not always true, but it doesn't affect the size that much, so
/// keep in mind that the size returned may be approximate in some cases.
/// The text inside this widget must be non-null before calling this method.
Size getSize({TextDirection? textDirection}) {
TextPainter tp = TextPainter(
text: TextSpan(text: data),
textDirection: textDirection ?? TextDirection.ltr)
..layout();
return tp.size;
}
}
And then when I define the PopupMenuButton
, I do this:然后当我定义PopupMenuButton
时,我这样做:
Widget _dropDownMenu({
required BuildContext context,
required String title,
required List<PopupMenuItem> items,
}) {
final text = Text(
title,
style: Theme.of(context).textTheme.bodyMedium,
);
final textSize = text.size;
return PopupMenuButton(
child: text,
itemBuilder: (context) => items,
offset: Offset(0, textSize.height),
);
}
It works, but I don't like it.它有效,但我不喜欢它。 I think there must be a better way to do this.我认为必须有更好的方法来做到这一点。
This is how it looks like right now:这是现在的样子:
I tried LayoutBuilder
, but it is returning infinite width constraints.我试过LayoutBuilder
,但它返回无限宽度约束。
Is there a more clean way of doing this?有没有更干净的方法来做到这一点?
It seems there is no other way except the approach I mentioned in the question, or to modify the source code of the PopupMenuButton
to make it accept an OffsetBuilder
as pskink mentioned.似乎除了我在问题中提到的方法之外没有其他方法,或者修改PopupMenuButton
的源代码以使其接受OffsetBuilder
提到的 OffsetBuilder。 This can be done like this (and there is full working example below):这可以像这样完成(下面有完整的工作示例):
PopupMenuButton
source code and copy it all into a new file custom_popup_menu.dart
(in this new file just remove all the imports and import them again as suggested by the IDE to fix them) go 到PopupMenuButton
源代码并将其全部复制到新文件custom_popup_menu.dart
(在这个新文件中只需删除所有导入并按照 IDE 的建议再次导入它们以修复它们)Offset _defaultOffsetBuilder(Size size) => Offset.zero;
将其添加到文件顶层的任何位置: Offset _defaultOffsetBuilder(Size size) => Offset.zero;
PopupMenuButton
class replace final Offset offset
with在PopupMenuButton
内部 class 将final Offset offset
替换为 /// The button size will be passed to this function to get the offset applied
/// to the Popup Menu when it is open. The top left of the [PopupMenuButton] is considered
/// as the origin of the coordinate system of this offset.
///
/// When not set, the Popup Menu Button will be positioned directly next to
/// the button that was used to create it.
final Offset Function(Size) offsetBuilder;
inside the constructor of this class replace this.offset
with this.offsetBuilder = _defaultOffsetBuilder,
在这个this.offset
的构造函数中用this.offsetBuilder = _defaultOffsetBuilder,
in the showButtonMenu
method of PopupMenuButtonState
class, replace在PopupMenuButtonState
class的showButtonMenu
方法中,替换
Rect.fromPoints(
button.localToGlobal(widget.offset, ancestor: overlay),
button.localToGlobal(
button.size.bottomRight(Offset.zero) + widget.offset,
ancestor: overlay),
),
Offset.zero & overlay.size,
);
with和
final offset = widget.offsetBuilder(button.size);
final RelativeRect position = RelativeRect.fromRect(
Rect.fromPoints(
button.localToGlobal(offset, ancestor: overlay),
button.localToGlobal(
button.size.bottomRight(Offset.zero) + offset,
ancestor: overlay),
),
Offset.zero & overlay.size,
);
... (imports)
import 'custom_popup_menu.dart' as pm;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => const MaterialApp(
debugShowCheckedModeBanner: false,
home: HomePage2(),
);
}
class HomePage2 extends StatelessWidget {
const HomePage2({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) => Scaffold(
body: Align(
alignment: const Alignment(0, -0.8),
child: Container(
decoration: BoxDecoration(border: Border.all(width: 2.0)),
child: pm.PopupMenuButton<String>(
child: const Text(
'Press Me',
style: TextStyle(color: Colors.black, fontSize: 50),
),
itemBuilder: (context) => [
_buildPopupMenuItem(),
_buildPopupMenuItem(),
_buildPopupMenuItem(),
],
color: Colors.red,
offsetBuilder: (buttonSize) => Offset(0, buttonSize.height),
),
),
),
);
pm.PopupMenuItem<String> _buildPopupMenuItem() {
return pm.PopupMenuItem(
child: Text(
'Press Me ${Random().nextInt(100)}',
style: const TextStyle(color: Colors.black, fontSize: 50),
),
onTap: () {},
);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.