简体   繁体   English

如何获得 PopupMenuButton(或任何小部件)的确切大小?

[英]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):这可以像这样完成(下面有完整的工作示例):

  • go to the 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 的建议再次导入它们以修复它们)
  • add this to anywhere top level in the file: Offset _defaultOffsetBuilder(Size size) => Offset.zero;将其添加到文件顶层的任何位置: Offset _defaultOffsetBuilder(Size size) => Offset.zero;
  • inside the PopupMenuButton class replace final Offset offset withPopupMenuButton内部 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, replacePopupMenuButtonState 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,
    );
  • Full Working Example:完整的工作示例:

... (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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM